Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 70fc573

Browse files
committedApr 10, 2024
docs: update domain model
1 parent 14dc7af commit 70fc573

File tree

1 file changed

+85
-258
lines changed

1 file changed

+85
-258
lines changed
 

‎domain-model/README.md

Lines changed: 85 additions & 258 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
---
22
title: Domain Model
3-
category: Architectural
3+
category: Structural
44
language: en
55
tag:
6-
- Domain
6+
- Business
7+
- Domain
78
---
89

10+
## Also known as
11+
12+
* Conceptual Model
13+
* Domain Object Model
14+
915
## Intent
1016

11-
Domain model pattern provides an object-oriented way of dealing with complicated logic. Instead of having one procedure that handles all business logic for a user action there are multiple objects and each of them handles a slice of domain logic that is relevant to it.
17+
The Domain Model pattern aims to create a conceptual model in your software that matches the real-world system it's designed to represent. It involves using rich domain objects that encapsulate both data and behavior relevant to the application domain.
1218

1319
## Explanation
1420

@@ -24,300 +30,121 @@ Programmatic Example
2430

2531
In the example of the e-commerce app, we need to deal with the domain logic of customers who want to buy products and return them if they want. We can use the domain model pattern and create classes `Customer` and `Product` where every single instance of that class incorporates both behavior and data and represents only one record in the underlying table.
2632

27-
Here is the `Product` domain class with fields `name`, `price`, `expirationDate` which is specific for each product, `productDao` for working with DB, `save` method for saving product and `getSalePrice` method which return price for this product with discount.
28-
2933
```java
30-
@Slf4j
31-
@Getter
32-
@Setter
33-
@Builder
34-
@AllArgsConstructor
35-
public class Product {
36-
37-
private static final int DAYS_UNTIL_EXPIRATION_WHEN_DISCOUNT_ACTIVE = 4;
38-
private static final double DISCOUNT_RATE = 0.2;
39-
40-
@NonNull private final ProductDao productDao;
41-
@NonNull private String name;
42-
@NonNull private Money price;
43-
@NonNull private LocalDate expirationDate;
44-
45-
/**
46-
* Save product or update if product already exist.
47-
*/
48-
public void save() {
49-
try {
50-
Optional<Product> product = productDao.findByName(name);
51-
if (product.isPresent()) {
52-
productDao.update(this);
53-
} else {
54-
productDao.save(this);
55-
}
56-
} catch (SQLException ex) {
57-
LOGGER.error(ex.getMessage());
58-
}
59-
}
34+
public class Customer {
35+
// Customer properties and methods
36+
}
6037

61-
/**
62-
* Calculate sale price of product with discount.
63-
*/
64-
public Money getSalePrice() {
65-
return price.minus(calculateDiscount());
66-
}
38+
public class Product {
39+
// Product properties and methods
40+
}
41+
```
6742

68-
private Money calculateDiscount() {
69-
if (ChronoUnit.DAYS.between(LocalDate.now(), expirationDate)
70-
< DAYS_UNTIL_EXPIRATION_WHEN_DISCOUNT_ACTIVE) {
43+
Data Access Objects (DAOs): These objects provide an abstract interface to the database. They are used to retrieve domain entities and save changes back to the database. In the provided code, CustomerDaoImpl and ProductDaoImpl are the DAOs.
7144

72-
return price.multipliedBy(DISCOUNT_RATE, RoundingMode.DOWN);
73-
}
45+
```java
46+
public class CustomerDaoImpl implements CustomerDao {
47+
// Implementation of the methods defined in the CustomerDao interface
48+
}
7449

75-
return Money.zero(USD);
76-
}
50+
public class ProductDaoImpl implements ProductDao {
51+
// Implementation of the methods defined in the ProductDao interface
7752
}
7853
```
7954

80-
Here is the `Customer` domain class with fields `name`, `money` which is specific for each customer, `customerDao` for working with DB, `save` for saving customer, `buyProduct` which add a product to purchases and withdraw money, `returnProduct` which remove product from purchases and return money, `showPurchases` and `showBalance` methods for printing customer's purchases and money balance.
55+
Domain Logic: This is encapsulated within the domain entities. For example, the Customer class has methods like buyProduct() and returnProduct() which represent the actions a customer can perform.
8156

8257
```java
83-
@Slf4j
84-
@Getter
85-
@Setter
86-
@Builder
8758
public class Customer {
59+
// ...
8860

89-
@NonNull private final CustomerDao customerDao;
90-
@Builder.Default private List<Product> purchases = new ArrayList<>();
91-
@NonNull private String name;
92-
@NonNull private Money money;
93-
94-
/**
95-
* Save customer or update if customer already exist.
96-
*/
97-
public void save() {
98-
try {
99-
Optional<Customer> customer = customerDao.findByName(name);
100-
if (customer.isPresent()) {
101-
customerDao.update(this);
102-
} else {
103-
customerDao.save(this);
104-
}
105-
} catch (SQLException ex) {
106-
LOGGER.error(ex.getMessage());
107-
}
108-
}
109-
110-
/**
111-
* Add product to purchases, save to db and withdraw money.
112-
*
113-
* @param product to buy.
114-
*/
11561
public void buyProduct(Product product) {
116-
LOGGER.info(
117-
String.format(
118-
"%s want to buy %s($%.2f)...",
119-
name, product.getName(), product.getSalePrice().getAmount()));
120-
try {
121-
withdraw(product.getSalePrice());
122-
} catch (IllegalArgumentException ex) {
123-
LOGGER.error(ex.getMessage());
124-
return;
125-
}
126-
try {
127-
customerDao.addProduct(product, this);
128-
purchases.add(product);
129-
LOGGER.info(String.format("%s bought %s!", name, product.getName()));
130-
} catch (SQLException exception) {
131-
receiveMoney(product.getSalePrice());
132-
LOGGER.error(exception.getMessage());
133-
}
62+
// Implementation of buying a product
13463
}
13564

136-
/**
137-
* Remove product from purchases, delete from db and return money.
138-
*
139-
* @param product to return.
140-
*/
14165
public void returnProduct(Product product) {
142-
LOGGER.info(
143-
String.format(
144-
"%s want to return %s($%.2f)...",
145-
name, product.getName(), product.getSalePrice().getAmount()));
146-
if (purchases.contains(product)) {
147-
try {
148-
customerDao.deleteProduct(product, this);
149-
purchases.remove(product);
150-
receiveMoney(product.getSalePrice());
151-
LOGGER.info(String.format("%s returned %s!", name, product.getName()));
152-
} catch (SQLException ex) {
153-
LOGGER.error(ex.getMessage());
154-
}
155-
} else {
156-
LOGGER.error(String.format("%s didn't buy %s...", name, product.getName()));
157-
}
158-
}
159-
160-
/**
161-
* Print customer's purchases.
162-
*/
163-
public void showPurchases() {
164-
Optional<String> purchasesToShow =
165-
purchases.stream()
166-
.map(p -> p.getName() + " - $" + p.getSalePrice().getAmount())
167-
.reduce((p1, p2) -> p1 + ", " + p2);
168-
169-
if (purchasesToShow.isPresent()) {
170-
LOGGER.info(name + " bought: " + purchasesToShow.get());
171-
} else {
172-
LOGGER.info(name + " didn't bought anything");
173-
}
174-
}
175-
176-
/**
177-
* Print customer's money balance.
178-
*/
179-
public void showBalance() {
180-
LOGGER.info(name + " balance: " + money);
181-
}
182-
183-
private void withdraw(Money amount) throws IllegalArgumentException {
184-
if (money.compareTo(amount) < 0) {
185-
throw new IllegalArgumentException("Not enough money!");
186-
}
187-
money = money.minus(amount);
188-
}
189-
190-
private void receiveMoney(Money amount) {
191-
money = money.plus(amount);
66+
// Implementation of returning a product
19267
}
19368
}
19469
```
19570

196-
In the class `App`, we create a new instance of class Customer which represents customer Tom and handle data and actions of that customer and creating three products that Tom wants to buy.
197-
71+
Application: The App class uses the domain entities and their methods to implement the business logic of the application.
19872

19973
```java
200-
// Create data source and create the customers, products and purchases tables
201-
final var dataSource = createDataSource();
202-
deleteSchema(dataSource);
203-
createSchema(dataSource);
204-
205-
// create customer
206-
var customerDao = new CustomerDaoImpl(dataSource);
207-
208-
var tom =
209-
Customer.builder()
210-
.name("Tom")
211-
.money(Money.of(USD, 30))
212-
.customerDao(customerDao)
213-
.build();
214-
215-
tom.save();
216-
217-
// create products
218-
var productDao = new ProductDaoImpl(dataSource);
219-
220-
var eggs =
221-
Product.builder()
222-
.name("Eggs")
223-
.price(Money.of(USD, 10.0))
224-
.expirationDate(LocalDate.now().plusDays(7))
225-
.productDao(productDao)
226-
.build();
227-
228-
var butter =
229-
Product.builder()
230-
.name("Butter")
231-
.price(Money.of(USD, 20.00))
232-
.expirationDate(LocalDate.now().plusDays(9))
233-
.productDao(productDao)
234-
.build();
235-
236-
var cheese =
237-
Product.builder()
238-
.name("Cheese")
239-
.price(Money.of(USD, 25.0))
240-
.expirationDate(LocalDate.now().plusDays(2))
241-
.productDao(productDao)
242-
.build();
243-
244-
eggs.save();
245-
butter.save();
246-
cheese.save();
247-
248-
// show money balance of customer after each purchase
249-
tom.showBalance();
250-
tom.showPurchases();
251-
252-
// buy eggs
253-
tom.buyProduct(eggs);
254-
tom.showBalance();
255-
256-
// buy butter
257-
tom.buyProduct(butter);
258-
tom.showBalance();
259-
260-
// trying to buy cheese, but receive a refusal
261-
// because he didn't have enough money
262-
tom.buyProduct(cheese);
263-
tom.showBalance();
264-
265-
// return butter and get money back
266-
tom.returnProduct(butter);
267-
tom.showBalance();
268-
269-
// Tom can buy cheese now because he has enough money
270-
// and there is a discount on cheese because it expires in 2 days
271-
tom.buyProduct(cheese);
272-
273-
tom.save();
274-
275-
// show money balance and purchases after shopping
276-
tom.showBalance();
277-
tom.showPurchases();
74+
public class App {
75+
public static void main(String[] args) {
76+
// Create customer and products
77+
// Perform actions like buying and returning products
78+
}
79+
}
27880
```
27981

28082
The program output:
28183

28284
```java
283-
17:52:28.690 [main] INFO com.iluwatar.domainmodel.Customer - Tom balance: USD 30.00
284-
17:52:28.695 [main] INFO com.iluwatar.domainmodel.Customer - Tom didn't bought anything
285-
17:52:28.699 [main] INFO com.iluwatar.domainmodel.Customer - Tom want to buy Eggs($10.00)...
286-
17:52:28.705 [main] INFO com.iluwatar.domainmodel.Customer - Tom bought Eggs!
287-
17:52:28.705 [main] INFO com.iluwatar.domainmodel.Customer - Tom balance: USD 20.00
288-
17:52:28.705 [main] INFO com.iluwatar.domainmodel.Customer - Tom want to buy Butter($20.00)...
289-
17:52:28.712 [main] INFO com.iluwatar.domainmodel.Customer - Tom bought Butter!
290-
17:52:28.712 [main] INFO com.iluwatar.domainmodel.Customer - Tom balance: USD 0.00
291-
17:52:28.712 [main] INFO com.iluwatar.domainmodel.Customer - Tom want to buy Cheese($20.00)...
292-
17:52:28.712 [main] ERROR com.iluwatar.domainmodel.Customer - Not enough money!
293-
17:52:28.712 [main] INFO com.iluwatar.domainmodel.Customer - Tom balance: USD 0.00
294-
17:52:28.712 [main] INFO com.iluwatar.domainmodel.Customer - Tom want to return Butter($20.00)...
295-
17:52:28.721 [main] INFO com.iluwatar.domainmodel.Customer - Tom returned Butter!
296-
17:52:28.721 [main] INFO com.iluwatar.domainmodel.Customer - Tom balance: USD 20.00
297-
17:52:28.721 [main] INFO com.iluwatar.domainmodel.Customer - Tom want to buy Cheese($20.00)...
298-
17:52:28.726 [main] INFO com.iluwatar.domainmodel.Customer - Tom bought Cheese!
299-
17:52:28.737 [main] INFO com.iluwatar.domainmodel.Customer - Tom balance: USD 0.00
300-
17:52:28.738 [main] INFO com.iluwatar.domainmodel.Customer - Tom bought: Eggs - $10.00, Cheese - $20.00
85+
17:52:28.690[main]INFO com.iluwatar.domainmodel.Customer-Tom balance:USD30.00
86+
17:52:28.695[main]INFO com.iluwatar.domainmodel.Customer-Tom didn't bought anything
87+
17:52:28.699[main]INFO com.iluwatar.domainmodel.Customer-Tom want to buy Eggs($10.00)...
88+
17:52:28.705[main]INFO com.iluwatar.domainmodel.Customer-Tom bought Eggs!
89+
17:52:28.705[main]INFO com.iluwatar.domainmodel.Customer-Tom balance:USD20.00
90+
17:52:28.705[main]INFO com.iluwatar.domainmodel.Customer-Tom want to buy Butter($20.00)...
91+
17:52:28.712[main]INFO com.iluwatar.domainmodel.Customer-Tom bought Butter!
92+
17:52:28.712[main]INFO com.iluwatar.domainmodel.Customer-Tom balance:USD0.00
93+
17:52:28.712[main]INFO com.iluwatar.domainmodel.Customer-Tom want to buy Cheese($20.00)...
94+
17:52:28.712[main]ERROR com.iluwatar.domainmodel.Customer-Not enough money!
95+
17:52:28.712[main]INFO com.iluwatar.domainmodel.Customer-Tom balance:USD0.00
96+
17:52:28.712[main]INFO com.iluwatar.domainmodel.Customer-Tom want to return Butter($20.00)...
97+
17:52:28.721[main]INFO com.iluwatar.domainmodel.Customer-Tom returned Butter!
98+
17:52:28.721[main]INFO com.iluwatar.domainmodel.Customer-Tom balance:USD20.00
99+
17:52:28.721[main]INFO com.iluwatar.domainmodel.Customer-Tom want to buy Cheese($20.00)...
100+
17:52:28.726[main]INFO com.iluwatar.domainmodel.Customer-Tom bought Cheese!
101+
17:52:28.737[main]INFO com.iluwatar.domainmodel.Customer-Tom balance:USD0.00
102+
17:52:28.738[main]INFO com.iluwatar.domainmodel.Customer-Tom bought:Eggs-$10.00,Cheese-$20.00
301103
```
302104
303105
## Class diagram
304106
305-
![](./etc/domain-model.urm.png "domain model")
107+
![Domain Model class diagram](./etc/domain-model.urm.png "domain model")
306108
307109
## Applicability
308110
309-
Use a Domain model pattern when your domain logic is complex and that complexity can rapidly grow because this pattern handles increasing complexity very well. Otherwise, it's a more complex solution for organizing domain logic, so shouldn't use Domain Model pattern for systems with simple domain logic, because the cost of understanding it and complexity of data source exceeds the benefit of this pattern.
111+
* Appropriate in complex applications with rich business logic.
112+
* When the business logic or domain complexity is high and requires a model that closely represents real-world entities and their relationships.
113+
* Suitable for applications where domain experts are involved in the development process to ensure the model accurately reflects domain concepts.
114+
115+
## Known Uses
116+
117+
* Enterprise applications (ERP, CRM systems)
118+
* Financial systems (banking, trading platforms)
119+
* Healthcare applications (patient records management)
120+
* E-commerce platforms (product catalogs, shopping carts)
121+
122+
## Consequences
123+
124+
Benefits:
310125
311-
## Related patterns
126+
* Improved Communication: Provides a common language for developers and domain experts, enhancing understanding and collaboration.
127+
* Flexibility: Encapsulates business logic within domain entities, making it easier to modify and extend without affecting other system parts.
128+
* Maintainability: A well-structured domain model can simplify maintenance and evolution of the application over time.
129+
* Reusability: Domain classes can often be reused across different projects within the same domain.
312130
313-
- [Transaction Script](https://java-design-patterns.com/patterns/transaction-script/)
131+
Trade-offs:
314132
315-
- [Table Module](https://java-design-patterns.com/patterns/table-module/)
316-
317-
- [Service Layer](https://java-design-patterns.com/patterns/service-layer/)
133+
* Complexity: Can introduce complexity, especially in simple applications where a domain model might be overkill.
134+
* Performance Concerns: Rich domain objects with complex behaviors might lead to performance bottlenecks, requiring careful optimization.
135+
* Learning Curve: Requires a good understanding of the domain and may involve a steep learning curve for developers unfamiliar with the domain concepts.
136+
137+
## Related Patterns
138+
139+
* [Data Access Object (DAO)](https://java-design-patterns.com/patterns/dao/): For abstracting and encapsulating all access to the data source.
140+
* [Service Layer](https://java-design-patterns.com/patterns/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.
141+
* [Repository](https://java-design-patterns.com/patterns/repository/): Mediates between the domain and data mapping layers, acting like an in-memory domain object collection.
142+
* [Unit of Work](https://java-design-patterns.com/patterns/unit-of-work/): Maintains a list of objects affected by a business transaction and coordinates the writing out of changes.
318143
319144
## Credits
320145
321-
* [Domain Model Pattern](https://martinfowler.com/eaaCatalog/domainModel.html)
146+
* [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://amzn.to/3vMCjnP)
147+
* [Implementing Domain-Driven Design](https://amzn.to/4cUX4OL)
322148
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321127420&linkId=18acc13ba60d66690009505577c45c04)
149+
* [Domain Model Pattern](https://martinfowler.com/eaaCatalog/domainModel.html)
323150
* [Architecture patterns: domain model and friends](https://inviqa.com/blog/architecture-patterns-domain-model-and-friends)

0 commit comments

Comments
 (0)
Please sign in to comment.