In the context of software engineering and system design, the term “domain” refers to the area of knowledge or activity that a software system needs to model or support. The “domain” includes all the relevant concepts, rules, processes, and data for the specific area of interest. For example, the domain of an order management system might include concepts such as orders, customers, products, payments, and shipments.
Key Domain Concepts
Domain Models: Represent the objects and entities relevant to the domain and their relationships. For instance, a domain model for an e-commerce system might include classes like Order, Customer, Product, and Payment.
Domain Logic: Includes the rules and behaviours that define how domain models operate. For example, rules on how an order state can change (CREATED -> SHIPPED -> DELIVERED) or the logic for calculating the total of an order.
Domain Services: Functions or classes that encapsulate complex logic not belonging to a single domain entity. For instance, a domain service might handle the logic of applying complex discounts to an order.
Domain Events: Notify other parts of the system that something significant has happened in the domain. For example, a domain event might be generated when an order is shipped.
Why Domain Modeling is Important
Clarity and Communication: A welldefined domain model helps clarify what the system does and facilitates communication between developers and domain experts (those with in-depth knowledge of the business area).
Maintainability: Organising code around domain concepts helps keep the code understandable and easier to modify and extend.
Reusability: Well-structured domain logic can be reused in different contexts within the system.
Example of Domain Logic
In the context of the Order class, domain logic might include:
- The rules for transitioning the state of an order (such as the
changeStatus
method that checks if a state transition is valid). - Calculating the total price of an order based on the added items.
- Conditions under which an order can be canceled.
Order Class Review in Domain Context
Here is a final version of the Order class, with domain logic for managing states and calculating the price:
@Getter
public class Order{
private final UUID id;
private OrderStatus status;
private BigDecimal price;
private final List< OrderItem > items = new ArrayList<>();
private static final Map< OrderStatus, Set< OrderStatus > > validTransitions = new EnumMap<>( OrderStatus.class );
static {
validTransitions.put( OrderStatus.CREATED, EnumSet.of( OrderStatus.SHIPPED, OrderStatus.CANCELLED ) );
validTransitions.put( OrderStatus.SHIPPED, EnumSet.of( OrderStatus.DELIVERED ) );
validTransitions.put( OrderStatus.DELIVERED, EnumSet.noneOf( OrderStatus.class ) );
validTransitions.put( OrderStatus.CANCELLED, EnumSet.noneOf( OrderStatus.class ) );
}
private Order( UUID id, List<OrderItem> items, OrderStatus status, BigDecimal price ) {
this.id = id;
this.items.addAll(items);
this.status = status;
this.price = price;
}
public static Order createNewOrder( UUID id, List< Product > products ){
Order order = new Order( id, new ArrayList<>(), OrderStatus.CREATED, BigDecimal.ZERO );
products.forEach(order::addItem);
return order;
}
public static Order restoreOrder( UUID id, List< OrderItem > items, OrderStatus status, BigDecimal price ) {
return new Order(id, items, status, price);
}
private void addItem( Product item ){
var orderItem = new OrderItem( item.getId(), item.getPrice() );
items.add( orderItem );
price = price.add( item.getPrice() );
}
public List< OrderItem > getItems(){
return Collections.unmodifiableList( items );
}
public void changeStatus( OrderStatus orderStatus ) {
Set<OrderStatus> allowedStatuses = validTransitions.get( this.status );
if (allowedStatuses == null || !allowedStatuses.contains(orderStatus)) {
throw new DomainException("Cannot change status from " + this.status + " to " + orderStatus);
}
this.status = orderStatus;
}
}
In this way, the Order class focuses exclusively on domain logic, maintaining the responsibility of ensuring valid state transitions and correctly calculating the total price. By isolating the business rules and encapsulating them within the domain entities, we enhance the maintainability, clarity, and robustness of our software system.
Conclusion
In this article, we explored the core concepts of domain-driven design, including domain models, logic, services, and events. We provided examples and implementation details to illustrate how these concepts come together to form a cohesive and well-structured domain layer.
In the next article, we will delve into the detailed implementation of Use Cases and their interaction with the domain layer. We will explore how use cases orchestrate application workflows, providing a structured and maintainable approach to handling business requirements within our system.
Comments are closed