Domain Driven Design Part 4 - Commands and Queries
Domain Driven Design Part 4 - Commands and Queries
Problem Solving
Relevant KSBs
C18 Fluent in written communications and able to articulate complex issues.
Objectives
• Explain what is meant by the acronym CQRS
• Explain the difference between queries and commands
• Explain and demonstrate how we implement persistence (infrastructure layer) in domain driven
design with Spring Boot
• Demonstrate how we implement entities for our aggregates
• Demonstrate how we can use interfaces for any dependencies in our solution
1
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
Contents
Relevant Learning Outcome ............................................................................................................... 1
Relevant KSBs ...................................................................................................................................... 1
Objectives ........................................................................................................................................... 1
Boundaries of Context for a Fictional Ordering System (reminder) ............................................... 4
Domain Model Operations ................................................................................................................. 5
Command-Query Responsibility Segregation (CQRS) Architectural Pattern .................................. 5
Comparing the Buyer Aggregate root (domain) with its infrastructure (how it is stored physically in a
table or similar) equivalent ..................................................................................................................... 7
Buyer and FullName in the Domain .................................................................................................... 7
FullName Value Object (domain) used by Buyer ............................................................................ 7
BuyerJpa (infrastructure) ........................................................................................................................ 8
BuyerJpa (entity) ................................................................................................................................. 8
Sample Data for the Buyer table (data.sql in resources) ................................................................ 9
IBuyerJpa (interface for BuyerJpa) ................................................................................................ 10
Comparing the PaymentMethod value object (domain) with its infrastructure equivalent ............ 11
Figure: copy of the attributes in our PaymentMethod value object ............................................ 11
ExpirationDate Value Object (domain – used by PaymentMethod) ............................................ 11
Figure: copy of the attributes in our ExpirationDate value object ............................................... 11
IdentifiedValueObject (domain/common) ................................................................................... 11
Figure: copy of the attributes in IdentifiedValueObject ............................................................... 11
PaymentMethodJpaValueObject (infrastructure) ........................................................................ 11
Sample Data for these tables (data.sql in resources) ................................................................... 12
PaymentMethodJpaValueObject (entity) ......................................................................................... 12
Infrastructure – Repositories ................................................................................................................ 14
CrudBuyerRepository (CrudRepository interface for BuyerJpa) ................................................... 14
BuyerRepository............................................................................................................................ 15
IBuyerRepository (interface for BuyerRepository so we can use the repository in a different
layer via IOC) ................................................................................................................................. 15
Looking at the application by considering each layer when we make a request ................................. 16
Data Transfer Object (DTO) – important information ...................................................................... 16
BuyerController (REST) ..................................................................................................................... 19
Query Handler method calls – findAll (application layer) ..................................................................... 20
getAllBuyersDetails (controller - UI layer) .................................................................................... 20
findAll method in BuyerQueryHandler (application layer) ........................................................... 20
findAll method in BuyerRepository (infrastructure layer) ............................................................ 20
2
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
Query Handler method calls – getBuyerById (buyer details only – not payments) ......................... 21
getBuyerById (controller).............................................................................................................. 21
findBuyerById method in BuyerQueryHandler (application layer) ............................................... 22
findById method in BuyerRepository (infrastructure layer) ......................................................... 22
Query Handler method calls – getPaymentDetailsForBuyer (payments associated with a specific
aggregate root) ................................................................................................................................. 23
Activity .............................................................................................................................................. 24
3
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
Source: https://www.microsoftpressstore.com/articles/article.aspx?p=2248811&seqNum=3
Ordering is the core domain here. Catalogue and CustomerBasket support Ordering along with
Identity to authenticate and assign role.
Buyer Order
Ordering Context
UserID
ProductID
ApplicationUser
Identity Context
Catalogue
Item
UserID
Catalogue Context
ProductID
CustomerBasket
Basket Context
Orange squares - are aggregates. Each will contain one or more entities and value
objects.
4
Black boxes are value objects used by a context that are ids from another context.
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
CQRS - Represent any kind of operations within the Bounded Context which either affect:
Why?
Focusing on the query side…. It can be difficult to query from repositories all the data that users
need to view, especially when we need to create views of data that cuts across a number of
aggregate types and instances.
“Separate the query responsibilities from all the responsibilities that execute pure commands on the
same model” (Vaughn Vernon, pg 139).
“Every method should be either a command that performs an action, or a query that returns data
to the caller, but not both. In other words, asking a question [query] should not change the answer.
More formally, methods should return a value only if they are referentially transparent and hence
possess no side effects.” (Bertrand Meyer)
5
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
Do you simply want to view some data? = Query (or read model)
o Request/retrieve data from the system in a form that is readable by the viewer.
o The data is retrieved via our repository into appropriate entities – it is NOT transformed into
the domain as it just needs to be returned in a form that can be manipulated by the viewer
e.g. as a JSON object (any object with Getter methods).
o It will not modify the data from the domain (as stored in the DB) as a result.
o We could generate a number of views that provide the data needed for a specific request
this is better than returning data that is not needed i.e. all the fields from an entity and
associated entities,
or assigning a specific interface to an entity that hides access to certain getters but
essentially has the same data underneath).
Some argue for the system to return the data – ‘as is’ (entity); others propose the use of a
DTO to old just the data requested in a form that is JSON compatible.
Figure 4: Diagram that illustrates CQRS in the sample system and the form of the data passed to layer
6
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
For more information consider Chapter 4 of Implementing Domain Driven Design by Vaughn
Vernon.
7
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
BuyerJpa (infrastructure)
id notice the id is explicitly identified (in our aggregate this is inherited from
Entity).
fullname_firstName notice how this is ‘flattened’ from the value object in Buyer to separate
fullname_surname fields to allow storage in our entity.
paymentMethods this is described as a list here because that allows all associated
PaymentMethodJpaValueObjects to be assigned to the List
paymentMethod when read by Spring from file – it is not saved (we can tell
this because the SQL is shown above plus it is not assigned a @Column).
BuyerJpa (entity)
@Entity(name="buyer")//Needed for custom queries
@Table(name="buyer")
@ToString
@Getter
@Setter
public class BuyerJpa implements IBuyerJpa {
@Id
@Column(name="id")
private String id;
@Column(name="fullname_firstname")
private String fullname_firstname;
@Column(name="fullname_surname")
private String fullname_surname;
8
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
protected BuyerJpa(){}
//Needed for DTO convertor – populate constructor)
protected BuyerJpa(String id,
String fullname_firstname,
String fullname_surname){
this.id = id;
this.fullname_firstname = fullname_firstname;
this.fullname_surname = fullname_surname;
paymentMethods = new ArrayList<>();
}
9
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
We are going to use interfaces for each bean in our solution; that means we need to define an
interface to match the entities (JPA, repositories, etc) – which involves replicating the method
signatures of these classes in our interfaces.
Important Tip: Whilst Lombok autogenerates most methods - we don’t see them due to use of
@Getter and @Setter but it is useful to generate these in BuyerJpa then export to your interface so
you have them defined correctly and can actually access them! (don’t forget to remove public and
method bodies when coping into your interface).
String getId();
String getFullname_firstname();
String getFullname_surname();
List<PaymentMethodJpaValueObject> getPaymentMethods();
10
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
Payment method inherits from IdentifiedValueObject which defines an id for our value object as we
need to save it in a separate table due to 1:m nature of PaymentMethod to Buyer. We don’t want to
include it right in our value object as it muddies the domain model (i.e. Value object rules say
identity in a domain is based on the state of a value object not a unique id).
IdentifiedValueObject (domain/common)
@Getter
@Setter
@NoArgsConstructor
public abstract class IdentifiedValueObject extends AssertionConcern{
private long id = -1; //surrogate id for ORM
PaymentMethodJpaValueObject (infrastructure)
Remember that we won’t have an entity defined for the Payment Method as it will be managed by
the Buyer entity (due to its relationship – discussed above and the Buyer repository.
11
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
create sequence - No need to insert the payment_method id as its set to auto-increment in the table
definition (see the previous page) but we do need to define how auto_increment works as it will
need to be managed by our Hiberate database (H2) rather than by an external DB as we done
previously.
INSERT INTO payment_method(card_number, cardholder_name, expiration_date,
security_number, buyer_id)
VALUES ('1111-1111-1111-1111','A Buyer','2024-12-31','123','0000'),
('2111-1111-1111-1111','Al Buyer','2024-01-31','234','0000');
PaymentMethodJpaValueObject (entity)
@Entity(name="payment_method")
@Table(name="payment_method")
@Setter
@Getter
@ToString
public class PaymentMethodJpaValueObject {
@Id
@Column(name="id")
@SequenceGenerator(name= "payment_method_sequence",
sequenceName = "payment_method_sequence_id",
allocationSize = 1)
@GeneratedValue(strategy=GenerationType.IDENTITY,
generator="payment_method_sequence")
@Column(name="card_number")
private String cardNumber;
@Column(name="cardholder_name")
private String cardholderName;
@Column(name="expiration_date")
private LocalDate expirationDate;
@Column(name="security_number")
private String securityNumber;
12
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
13
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
Infrastructure – Repositories
We know from day 1 that we only need to define an interface for a repository and Spring will create
a proxy object that it can inject into any class that needs to use it – the problem is that we want to
decouple each of our layers which means we need to define interfaces for the objects that we need
to refer to and then use that interface on the class (collaborator object - which is our dependency
being injected).
Question: How do we do this when we only define an interface to start with rather than a
concretion?
Answer: We implement that repository interface (in the infrastructure layer) and apply another
interface based on its methods – then include that interface in the layer that needs the dependency
– thus decoupling the repository from the class that needs it.
We also need to refer to our entities (e.g. BuyerJpa) in our application layer so we need to define an
interface for this – hence the use of IBuyerJpa here (rather than BuyerJpa).
@Query("FROM buyer")
Iterable<IBuyerJpa> findAllBuyers();
@Query("FROM buyer")
We need to explicitly define our query as returning an interface type gets confused with default
findAll as it expects to return BuyerJpa – we would otherwise get an
UnsatisfiedDependencyException.
14
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
BuyerRepository
Implementation (has IBuyerRepository interface so I can use it in my QueryHandler and Application
Services) when calling the repository there.
@Component //Will not find this class without this annotation when trying
to inject
@AllArgsConstructor
public class BuyerRepository implements IBuyerRepository {
private CrudBuyerRepository repository;
Iterable<IBuyerJpa> findAllBuyers();
Application.properties (reminder of port so the inclusion in the end point after localhost makes
sense)
server.port=8900
15
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
“An object that carries data between processes in order to reduce the number of method calls.”
…” DTOs are called Data Transfer Objects because their whole purpose is to shift data in expensive
remote calls.“ (Fowler, 2002)
“A DTO is nothing more than a container class that exposes properties but no methods. A DTO is
helpful whenever you need to group values in ad hoc structures for passing data around.” (Esposito,
2008, for Microsoft).
This pattern implements a class that will be used to transfer/carry data – as an object – between our
UI and service layers when it is received by the service layer it will be converted into an entity
(mapped to the table structure) before being passed to the repository layer.
16
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
Source: https://docs.microsoft.com/en-us/archive/msdn-magazine/2009/brownfield/pros-and-cons-
of-data-transfer-objects#id0080022
DTOs are typically plain objects (in Java we call them POJO) that have zero argument constructors
and are serializable to allow communication remotely.
Note: Some argue that DTOs should only be used at the controller layer and be converted to/from
entities there - rather than in the service layer (but this violates what a controller’s responsibility is
really about – namely to interact the UI and as you can see above many (including Microsoft)
support the service layer (our service layer is split up as query handlers and command handlers) as
the place that DTOs are converted to/from an entity.
17
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
• Potentially require multiple calls to move more than one object which is an expensive
approach to implementation (when we could have a single call, passing a DTO, that passes
all the data that we require).
• “use when you have a significant mismatch between the model in your presentation layer
and the underlying domain model” (Fowler)
• We do not want to expose the entire entity – memory/redundancy or security reasons.
• Client is separated from the entities so any changes to entities are not impacting on the
client code = reduced coupling between layers.
• Client is separated from the entities so any changes to the UI can be handled by DTOs rather
than entities.
• Avoids circular references when serialising which would be a problem when working directly
with entities.
• Additional coding overhead to create/manage DTOs for each entity (if they were a large
number of them) as well as convertor logic (although this can be done via Mapper classes
which negates convertors in favour of 3rd party dependency code).
• “You can do without DTOs only if the presentation layer and the service layer are co-located
in the same process. In this case, you can easily reference the entity assembly from within
both layers without dealing with thorny issues such as remoting and data serialization”.
Middle Ground
• For large projects where presentation and service layers are in the same process (as
discussed above) – use entities where possible (but it increases coupling).
18
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
BuyerController (REST)
• Is going to communicate with the Query Handler (and next week also with the Command
Handler).
• As the Query Handler (and Command Handler) exist at the next layer- we need to define
interfaces for these and interact with these (rather than concretions).
• @AllArgsConstructor injects dependencies at run time - but as they are interfaces we must
annotate the actual classes they refer to with @Service (or @Component) in order for Spring
to find them.
@RequestMapping("/buyers")
@RestController
@AllArgsConstructor
public class BuyerController {
private IBuyerQueryHandler queryHandler;
19
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
For testing purposes only I am going to set up an end point to find all aggregates – would access
this via: http://localhost:8900/buyers/findAll this is a GET request.
The result of findAll is a set of Iterable objects – we can use <?> to avoid identifying a specific type
here.
findAllBuyers will return an Iterable set of BuyerJpa objects indirectly referenced via IBuyerJpa.
@Component //Will not find this class without this annotation when trying
to inject
@AllArgsConstructor
public class BuyerRepository implements IBuyerRepository {
private CrudBuyerRepository repository;
20
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
Query Handler method calls – getBuyerById (buyer details only – not payments)
Buyer id needs to be identified so we assign a path variable name – and then use this in our call to
the query handler. As the id may be invalid the repository returns the result in an Optional for
safety.
http://localhost:8900/buyers/0000
getBuyerById (controller)
Our value e.g. 0000 is passed as an argument after /buyers/
21
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
Result from the repository is converted from an entity into a DTO to return the view that the user is
requesting.
public Optional<OrderDTO> findByBuyerId(String buyer_id){
Conversion code for entity -> DTO is currently contained in the query handler – it could be separated
into a separate class.
Notice that the conversion omits to use any PaymentMethod data associated with our buyer – as
that is not part of the request – instead it just copies the buyer information.
//Create class based projection for Buyer using DTO - could be separate
class
private Optional<BuyerDTO> convertBuyerDetailsToDTO(IBuyerJpa buyer) {
BuyerDTO buyerDTO = new BuyerDTO(buyer.getId(),
buyer.getFullname_firstname(), buyer.getFullname_surname());
return Optional.of(buyerDTO);
}
22
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
return result;
}
Conversion code for entity -> DTO is currently contained in the query handler – it could be separated
into a separate class but here it is part of the application class.
//Create class based projection for Payment using DTO - could be separate
class
private List<PaymentMethodDTO> convertPaymentDetailsToDTO(IBuyerJpa buyer){
List<PaymentMethodDTO> paymentMethodDTOs = new ArrayList<>();
return paymentMethodDTOs;
}
23
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries
Activity
Review the Ordering aggregate.
Consider the UI, application and infrastructure classes and interfaces in use – and compare to the
Buyer aggregate.
24
Phil James