The heart of the kitchen
Inside the application core, code is organized into two main layers:
Domain layer: This acts like a recipe book for the business. It contains your entities (like a PizzaOrder with a unique ID and lifecycle) and ensures your business invariants are never broken. For example, an order cannot be "out for delivery" if it hasn't been baked.
Application layer: This is the "kitchen manager". It coordinates workflows by fetching data through ports, invoking domain logic and saving results, but it does not contain the business rules itself.
Why this matters
By separating business logic from infrastructure, you gain three major advantages:
Plug-and-play: You can swap databases or SMS providers in an afternoon without touching your core logic.
Testable: You can test the entire checkout flow without actually charging a credit card.
Resilient: External system failures become easier to isolate and manage.
Bringing order with domain-driven design
If our core logic is a tangled mess of spaghetti code, the architectural boundary won't save us. To ensure our application remains maintainable as it scales from a local shop to a global franchise, we need to apply domain-driven design (DDD) to organize the kitchen. While hexagonal architecture protects the system boundary, DDD helps structure the business logic inside that boundary.
Entities and value objects
Inside the domain layer, not every piece of data is treated the same. Distinguishing between entities and value objects is our first step toward clarity.
An entity has a unique identity that persists over time. For example, "Order #542" is an entity. Even if some order details change, it remains the same order because its identity persists over time.
A value object, on the other hand, is defined only by its attributes. "Extra cheese" doesn't need a unique ID; if you swap one extra cheese for another, nothing changes. By treating these as immutable value objects, we reduce bugs and make our code easier to reason about.
Defining the boundaries: The aggregate root
In a busy kitchen, you want all changes related to an order to pass through a single controlled entry point. In DDD, that entry point is the aggregate root. For example, status updates, line items and total calculations should all be managed through the order aggregate. It’s the gateway to all internal components (like individual pizzas or line items). Any change to the state of the order must go through the aggregate root to ensure business invariants are always enforced.
The decision matrix
One of the biggest challenges in software design is deciding where a specific rule belongs. A useful rule of thumb is:
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.