domain logic patterns transaction script domain model table module service layer
TRANSCRIPT
ENTERPRISE APPLICATION ARCHITECTURE
Domain Logic Patterns
Transaction Script Domain Model Table Module Service Layer
Transaction Script
Organizes business logic by procedures where each procedure handles a single request form the presentation
TS: How It Works
Each business transaction corresponds to one transaction script Business transaction: Book a hotel room Tasks: check room availability, calculate rates,
update the database – all handled in one BookARoom script.
Transaction scripts access the database directly Don’t call any logic in the presentation layer Organization
Each script is a procedure Related scripts are enclosed in one class Each script is in one class.
TS: Architecture
TransactionScriptbusinessTransaction1()businessTransaction2()businessTransaction3()
Gateway
findDataForBusinessTransaction1(sql_query)insertRecordsForBusinessTransaction1(sql_insert, items)updateRecordsForBusinessTransaction2(sql_update, items)…
sql_query= “ SELECT * FROM table 1…”sql_insert = “INSERT into tablei…”sql_update = “ UPDATE tablei …”sql_delete = “DELETE FROM …”
DB
TS: When to use it
When the domain logic is very simple When transactions do not have a lot
of overlap in functionality
Example Revenue Recognition A contract is signed for one product The revenue of a contract may not be recognized
right away. Different types of product may have different
revenue recognition schedule Three types of product
Word Processor: revenue recognized right away Spreadsheet: 1/3 today, 1/3 in 60 days, 1/3 in 90 days. Database: 1/3 today, 1/3 in 30 days, 1/3 in 60 days.
Product
Nametype
Contract
date _signedrevenue
RevenueRecognitionAmount
date
11 * *
TS: Example
RecognitionServicecalcRecognitions(contract#)recognizedRevenue(contract#, date)createContract(id, revenue, prod_id, date)…
DatabaseGateway
findContract(contract#)findRecognitionsFor(contract#, date)insertRecognition(contract#, revenue, date)…
Sql_findContract = “ SELECT * FROM Contract WHERE id = ?”Sql_findRecogns = “select* from recog Where cid=? and date<?”Sql_insertContract = “INSERT into contract…”
DB
createContract()
Sequence Diagram: test cases
:RecognitionService :DatabaseGateway
:Tester
insertContract()INSERT into contract …
:Database
calcRecognitions()insertRecognition()
INSERT iinto Recog…
recognizedRevenue()findRecognitionsFor()
SELECT * FROM …
insertRecognition()INSERT iinto Recog…
INSERT iinto Recog…insertRecognition()
TS: Example
class Gateway { static String findRecogns = “SELECT * FROM revenueRecognition WHERE contract = ? And date <= ?”; static String findContract = “SELECT * FROM contract c, product p WHERE c.id = ? And c.pid = p.id”; public ResultSet findRecognitionsFor(int contrno, Date d) { PreparedStatement s = db.prepareStatement(findRecongs); s.setInt(1, contrno); s.setDate(2, d); ResultSet result = s.executeQuery(); return result; } public ResultSet findContract(int contrno) { PreparedStatement s = db.prepareStatement(findContract); s.setInt(1, contrno); ResultSet result = s.executeQuery(); return result; }}
TS: Exampleclass RecognitionService { private Gateway gw = new Gateway(); public Money recognizedRevenue(int contrno, Date d) { Money Result = Money.dollar(0); ResultSet rs = gw.findRecognitionsFor(contrno, d); while (rs.next()) { result = result.add(rs.getBigDecimal(“amount”)); } return result; } public void calculateRevenueRecognitions(int contrno) { ResultSet contrs = gw.findContract(contrno); totalRevenue = contrs.getBigDecimal(“revenue”); dates = contrs.getDate(“date_signed”); type = contrs.getChar(“type”); if (type == ‘S’) { db.insertRecognition(contrno, totalRevenue/3, date); db.insertRecognition(contrno, totalRevenue/3, date+60); db.insertRecognition(contrno, totalRevenue/3, date+90); } else if (type = ‘W’) { db.insertRecognition(contrno, totalRevenue, date); } else if (type == ‘D’ { ... }...
Domain Model
An object model of the domain that incorporates both behavior and data
DM: Revenue Recognition
ProductNametype
Contract
date _signedrevenue
RevenueRecognitionAmount
date
1
1*
*
recognizedRevenue(date)calculateRecognitions()
calcRecognitions(contrct)
RecognitionStrategy
CompleteRecognitionStrate
gy
*1
Three-wayRecognitionStrate
gy
calcRecognitions(contrct)
isRecognizableBy(date)
DB
DM: Exampleclass RevenueRecognition { private Money amount; private Date date; public RevenueRecognition(Money amnt, Date d) {...} public Money getAmount() { return amount; } public boolean isRecognizableBy(Date date) { return this.date.before(date) || this.date.equals(date); }...}class Contract { private List revenueRecognitions = new ArrayList(); public Money recognizedRevenue(Date date) { Money result = Money.dollar(0); Iterator it = revenueRecognitions.iterator(); while (it.hasNext()} { RevenueRecognition r = (RevenueRecognition)it.next(); if (r.isRecognizableBy(date)) result = result.add(r.getAmount()); } return result; }...
DM: Exampleclass RevenueRecognition { private Money amount; private Date date; public RevenueRecognition(Money amnt, Date d) {...} public Money getAmount() { return amount; } public boolean isRecognizableBy(Date date) { return this.date.before(date) || this.date.equals(date); }...}class Contract { private List revenueRecognitions = new ArrayList(); public Money recognizedRevenue(Date date) { Money result = Money.dollar(0); Iterator it = revenueRecognitions.iterator(); while (it.hasNext()} { RevenueRecognition r = (RevenueRecognition)it.next(); if (r.isRecognizableBy(date)) result = result.add(r.getAmount()); } return result; }...
DM: Exampleclass Contract { private Product product; private Money amount; private Date dateSigned; private long id; public Contract(Product p, Money amnt, Date d) {...} public void addRecognition(RevenueRecognition rr) { revenueRecognitions.add(rr); } public Date getDateSigned() { return dateSigned; } public void calcRecognitions() { product.calcRecognitions(this); }...}interface RecognitionStrategy { public void calcRevenueRecognitions(Contract c);}
DM: Exampleclass CompleteRecognitionStrategy implements ... { public void calcRevenueRecognitions(Contract c) { c.addRecognition(new RevenueRecognition( c.getAmount(), c.getDateSigned()); }}class ThreeWayRecognitionStrategy implements ... { private int firstRecognitionOffset; private int secondRecognitionOffset; public ThreeWayRecognitionStrategy(int offset1, int offset2) { this.firstRecognitionOffset = offset1; this.secondRecognitionOffset = offset2; } public void calcRevenueRecognitions(Contract c) { c.addRecognition(new RevenueRecognition( c.getAmount()/3, c.getDateSigned()); c.addRecognition(new RevenueRecognition( c.getAmount()/3, c.getDateSigned()+offset1); c.addRecognition(new RevenueRecognition( c.getAmount()/3, c.getDateSigned()+offset2); } ... }
DM: Exampleclass Product { private String name; private RecognitionStrategy recogStrategy; public Product(String name, RecognitionStrategy rs) { this.name = name; this.recogStrategy = rs; } public void calcRecognitions(Contract c) { recogStrategy.calcRecognitions(c); } public static Product newWordProcessor(String name) { return new Product(name, new CompleteRecognitionStrategy()); } public static Product newSpreadsheet(String name) { return new Product(name, new ThreeWayRecognitionStrategy(60, 90)); } public static Product newDatabase(String name) { return new Product(name, new ThreeWayRecognitionStrategy(30, 60)); }}
DM: Exampleclass Tester { public static void main(String[] args) { Product word = Product.newWordProcessor(“IntelWord”); Product calc = Product.newSpreadsheet(“calc II”); Product db = Product.newDatabse(“DB IV”);
Date today = System.today(); Contract c1 = new Contract(word, 300000, today); c1.calcRecognitions(); Contract c2 = new Contract(calc, 24000, today); c2.calcRecognitions(); // sequence diagram – next slide Contract c3 = new Contract(db, 540000, today); c3.calcRecognitions();
System.out.println(c1.recognizedRevenue(today + 10)); System.out.println(c2.recognizedRevenue(today + 70)); System.out.println(c3.recognizedRevenue(today + 80)); }}
product:Product
c2.:Contract
calcRecognitions()
calcRecognitions(c2)
DM: Sequence Diagram: c2.calcRecognitions()
rr1:RevenueRecognition
recogStrategy:RecognitionStrategy
(amount/3, date)
calcRecognitions(c2)
Mount = getAmount()
date = getDateSigned()
addRecognition(rr1)rr2:RevenueRecognition
rr3:RevenueRecognition
addRecognition(rr2)
addRecognition(rr3)
(amount/3, date + 60)
(amount/3, date + 90)
:Tester
c2.:Contract
recognizedRevenue(date)
isRecognizableBy(date)
DM: Sequence Diagram: c2.recognizedRevenue()
rr[0]:RevenueRecognition rr[1]:RevenueRecognition rr[2]:RevenueRecognition
:Tester
getAmount()
isRecognizableBy(date)
getAmount()
isRecognizableBy(date)
return result
Table Module
A single instance that handles the business logic for all rows in a database table or view Each module is responsible for all the
CRUD operations on the corresponding table.
No other modules are supposed to CRUD directly on the table
Each module also includes business logic that is tightly related to the table.
TM: Architecture
Table_1TableModule_1
CRUD operations on Table_1Business Logic related to Table_1
Attributes
Table_2TableModule_2
CRUD operations on Table_2Business Logic related to Table_2
Attributes
Table_nTableModule_n
CRUD operations on Table_nBusiness Logic related to Table_n
Attributes
Database
TM: Example - Tables
Product
Contract RevenueRecognitionId: Number
dateSigned: Daterevenue: Numberprod_id: Number (FK)
Id: Numbername: Stringtype: String
Id: Numberamount: Numberdate: Date c_id: Number (FK)
TM: Example - Modules
Product
Contract RevenueRecognition
Insert(cid, revenue, prod_id, date)calculateRecognitions(c_id)
getProductType(prod_id)
Insert(c_id, amount, date)recognizedRevenue(c_id, date)
DB
:Product
:Contract
getContract(id)
getProductID()
TM: Sequence Diagram: calcRecognitions()
:RevenueRecognition
:Tester
contract:ResultSet
getProductType(pid)
calcRecognitions(cid)
insert(cid, revenue/3, date)
insert(cid, revenue/3, date+60)
insert(cid, revenue/3 date+90)
getRevenue()
getDateSigned()
return result
DB
SELECT
SELECT
INSERT
INSERT
INSERT
recognizedRevenue(c_id, date)
getAmount()
TM: Sequence Diagram: recognizedRevenue()
:RevenueRecognition:Tester
recognitions:ResultSet
:Contract
getRecognitions(c_id, date)
getAmount()
getAmount()
return result
DB
SELECT
Service Layer
Defines an application’s boundary with a layer of services that establishes a set of available operations and coordinates the application’s response in each operation. Two type of business logic
Domain logic: pure logic of the business domain E.g., calculating revenue recognitions of a contract
Application logic: application responsibilities E.g., notifying administrators and integrated
applications, of revenue recognition calculations
TM: Example - Modules
Domain Model
Service Layer
Data Loader
DB
User Interfaces
Integration
Gateways
SL: Architecture
Domain logic: Domain model layer Application logic: Service layer Service layer:
Operation Scripts – A set of classes that implement application logic but delegate to domain model classes for domain logic.
Clients interact with the operation scripts Operation scripts for a subject area are
encapsulated in a class named SubjectService.
SL: Services and Operations Determined by the needs of clients Derived from use case models
Data validation CRUD operations on domain objects Notification of people or other integrated
applications All responses must be coordinated and
transacted automatically by the service layer
SL: When to Use It
When there are many different kinds of clients
When the response may involve application logic that needs to be transacted across multiple transactional resources
SL: Example
Revenue Recognition New requirements: once revenue
recognitions are calculated, it must Email a notification to contract
administrators Publish a message to notify other
integrated applications
SL: Example
RecognitionServicecalcRevenueRecogs(contr#)recognizedRevenue(contr#, date)
ApplicationService
EmailGateway
sendEmail(toAddr, subj, body)
getEmailGateway(): EmailGatewaygetIntegrationGateway(): IntegrationGateway
IntegrationGateway
publishRevenueRecogs(contract)
Contract ProductRevenueRecognition
Domain Model
SL: Exampleclass RecognitionService extends ApplicationService { public void calcRevenueRecogs(contractNo) { Transaction trans = Transaction.getNewTransaction(); trans.begin();
// delegate to domain objects Contract contract = Contract.getContract(contractNo); contract.calcRecognitions(); Contract c2 = new Contract(calc, 24000, today);
// interact with transactional sources getEmailGateway().sendEmail(contract.getAdminEmail(), “RE: contract revenue recognitions”, contract.getId() +”Recognitions calculated”); getIntegrationGateway().publishRevenueRecogs(contract);
trans.commit(); }}
Domain Logic: Summary
Transaction Script One script per user request/action Good for simple, no-overlapping business logic
Domain Model A set of interrelated objects for business logic Good for application with complex business logic
Table Module A module for the CRUD operations and business logic for a
table in DB Compromise between Transaction Script and Domain Model
Service Layer Application logic is separated into a new layer from domain
logic Good for applications that have complex application logic –
interacting with multiple transactional resources