Layers
The Eclipse Cargo Tracker application is layered as illustrated by the following picture.
As you can see, there are three vertical layers: interfaces, application, and domain, each supported by different kinds of infrastructure. In the application, the package naming reflects these layers.
Interfaces
This layer holds everything that interacts with other systems (including humans :-), such as REST web services, web UIs, inbound external messaging endpoints, and batch processes. It handles interpretation, input validation, and translation of incoming data. It also handles serialization of outgoing data, such as HTML or JSON across HTTP to web browsers or web service clients, or DTO classes and distributed facade interfaces for remote clients.
The
org.eclipse.cargotracker.interfaces
package holds all the interface classes for the application. We have two Web
UI interfaces - one for
booking
and the other for
tracking,
as well as one
REST interface
and one
scheduled file processor
for ingesting handling events. The web interfaces are written in
HTML/JavaScript/Jakarta Faces (JSF), the REST interface uses Jakarta
REST (JAX-RS), while the file processor uses @Scheduled
/Jakarta Batch.
Modern web application frameworks naturally enforce MVC style separation of
concerns, so there is not that much heavy lifting you will need to do. In
addition, you can enforce a facade layer if necessary (e.g., your UI team is
sufficiently separated from the application services team or you wish to
remain strictly agnostic of the UI layer). Otherwise, you can use simple
adapters to adapt domain classes to your UI view. We demonstrate both
techniques in the application using well-understood existing design patterns
such as Facade, DTO, Assembler, and Adapter (in addition to MVC of course).
Other than Jakarta Faces (JSF) and REST (JAX-RS), other Jakarta EE technologies typically used in the interface layer are Jakarta Security, CDI, Validation, JSON Binding, Batch, and Messaging.
Application
The application layer is responsible for driving the workflow of the application, matching the business use cases at hand. These operations are interface-independent and can be both synchronous or asynchronous. This layer is well suited for spanning transactions, high-level logging, and security.
Note, the application layer is very thin in terms of domain logic - it merely coordinates the domain layer objects to perform the actual work. This is very different from traditional tiered architectures that tend to look more procedural with a lot of business logic in the application layer.
The org.eclipse.cargotracker.application package contains all the application classes. The application layer is typically implemented using Enterprise Beans (EJB) or CDI beans. Other Jakarta EE APIs likely to be used in this layer are Jakarta Security, Transactions (JTA), Concurrency, and Interceptors.
Domain
The domain layer is the heart of the software, and this is where the interesting stuff happens. There is one package per aggregate and each aggregate includes entities, value objects, domain events, a repository interface, and sometimes factories (see the Characterization section for details). The aggregate roots are Cargo, HandlingEvent, Location, and Voyage.
The core of the business logic, such as determining whether a handling event should be registered and how the delivery of a cargo is affected by handling, belongs here. The structure and naming of aggregates, classes, and methods in the domain layer should follow the real world as closely as possible. You should be able to explain to a business domain expert how this part of the software works by drawing a few simple diagrams and using the actual class and method names of the source code. Note that it is not uncommon for some domain classes to simply model data and not contain any business logic.
The domain layer is usually implemented primarily using Jakarta Persistence (JPA), Validation, and CDI.
Infrastructure
In addition to the three vertical layers, there is also the infrastructure. As the picture shows, it supports all of the three layers in different ways, sometimes facilitating communication between the layers. In the most common case, the infrastructure layer consists of Repository implementations interacting with a database. In simple terms, the infrastructure consists of everything that exists independently of our application: external/third-party resources, persistence/database engines, messaging back-ends, legacy systems, and so on.
All of the infrastructure classes are in the org.eclipse.cargotracker.infrastructure package. We use Jakarta Persistence (JPA), CDI, Messaging (JMS), and the REST (JAX-RS) client API in this layer. The application will mostly have Jakarta Persistence (JPA) code in this layer encapsulated with thin CDI bean Repository classes. Other Jakarta EE APIs commonly used in this layer include Jakarta Mail.
Are Layers Mandatory?
Very few things in DDD are mandatory, and layering is no exception. You should use layers if you believe they add value in keeping your application maintainable. Indeed, even if you do choose to use layers, you do not need to use them everywhere in your application as an absolute requirement.
If you look closely at the application as well as the opening image in this section, it is perfectly OK to skip layers per use case. For example, it is quite common for the interface layer to skip the application layer and communicate directly with the domain layer. In many other cases, having a layer will clearly add value by separating concerns, improving testability, reducing coupling, and enhancing semantic clarity. A helpful example of this is the BookingService implementation.