0% found this document useful (0 votes)
3 views

Domain Driven Design Part 4 - Commands and Queries

This document discusses the implementation of queries in enterprise application development using Domain Driven Design (DDD) and the Command-Query Responsibility Segregation (CQRS) architectural pattern. It outlines the objectives of understanding the differences between queries and commands, implementing persistence with Spring Boot, and utilizing interfaces for dependencies. Additionally, it provides examples of domain models, aggregates, and infrastructure layers, specifically focusing on the Buyer and PaymentMethod entities.

Uploaded by

oladejiasher
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

Domain Driven Design Part 4 - Commands and Queries

This document discusses the implementation of queries in enterprise application development using Domain Driven Design (DDD) and the Command-Query Responsibility Segregation (CQRS) architectural pattern. It outlines the objectives of understanding the differences between queries and commands, implementing persistence with Spring Boot, and utilizing interfaces for dependencies. Additionally, it provides examples of domain models, aggregates, and infrastructure layers, specifically focusing on the Buyer and PaymentMethod entities.

Uploaded by

oladejiasher
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 24

Enterprise Application Development – Domain Driven Design – part 4 - Queries

Relevant Learning Outcome


1. DESIGN AN ENTERPRISE APPLICATION, CRITICALLY EVALUATING Analysis
ALTERNATIVES AND JUSTIFYING SELECTIONS.
Communications

Problem Solving

Relevant KSBs
C18 Fluent in written communications and able to articulate complex issues.

SE9 How to operate at all stages of the software development lifecycle.

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

Boundaries of Context for a Fictional Ordering System (reminder)


A subdomain in the problem space is mapped to a bounded context in the solution space. A
bounded context is an area of the application that requires its own ubiquitous language and its own
architecture. Or, put another way, a bounded context is a boundary within which the ubiquitous
language is consistent. A bounded context can have relationships to other bounded contexts.

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

Figure 1: Bounded Contexts that form our case study code

Blue ellipses – are bounded contexts.

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

Domain Model Operations


Command-Query Responsibility Segregation (CQRS) Architectural Pattern

CQRS - Represent any kind of operations within the Bounded Context which either affect:

• Change the state of the aggregate/entity (command)


• or, query (view) the state of the aggregate/ entity (query)

Figure 2: Commands/Queries within the Originations Bounded Context

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.

Solution = CQRS – how does it work?

“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

CQRS Principles elaborating on what was written above:

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 3: Commands from Clients travel ‘one way’ to the command

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.

Comparing the Buyer Aggregate root (domain) with its infrastructure


(how it is stored physically in a table or similar) equivalent
Buyer and FullName in the Domain
We can see that our Buyer consists of:

• FullName – first name and surname of the Buyer


• PaymentMethods – one or more methods detailing Payment Methods that a particular
Buyer has
public class Buyer extends Entity{
private FullName fullName;
//Omitting address for simplicity
//Omitting card type of payment for simplicity
private List<PaymentMethod> paymentMethods;
Figure 5: copy of the attributes in our Buyer entity

FullName Value Object (domain) used by Buyer


public class FullName extends ValueObject {
private String firstName;
private String surname;
Figure 6: copy of the attributes in our FullName value object

7
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries

BuyerJpa (infrastructure)

No payment methods are not included in this SQL table.


Why? they are a repeating group – so they need to be stored separately to avoid repeating the
buyer name each time we record a payment method.
CREATE TABLE buyer(
id VARCHAR PRIMARY KEY,
fullname_firstName VARCHAR NOT NULL,
fullname_surname VARCHAR NOT NULL
);

Table 1: Columns in the Buyer entity and their meaning

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;

@OneToMany(mappedBy = "buyer", cascade = {CascadeType.ALL})


private List<PaymentMethodJpaValueObject> paymentMethods;

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<>();
}

//Factory method – return entity using fields


public static BuyerJpa buyerJpaOf(String id,
String fullname_firstname,
String fullname_surname) {
return new BuyerJpa(id, fullname_firstname, fullname_surname);
}

Sample Data for the Buyer table (data.sql in resources)


Create a sample record to view/test the behaviour of the queries..
INSERT INTO buyer (id, fullname_firstName, fullname_surname)
VALUES('0000', 'firstname1', 'surname1');

9
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries

IBuyerJpa (interface for BuyerJpa)

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).

Note: We don’t need to define an interface/entity for the PaymentMethodJpaValueObject as it is


accessed via our aggregate only – meaning we retrieve it via our BuyerRepository – there is no
PaymentMethodRepository refined as our model is aggregate centric.

public interface IBuyerJpa {

String getId();

String getFullname_firstname();

String getFullname_surname();

List<PaymentMethodJpaValueObject> getPaymentMethods();

void setId(String id);

void setFullname_firstname(String fullname_firstname);

void setFullname_surname(String fullname_surname);

10
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries

Comparing the PaymentMethod value object (domain) with its infrastructure


equivalent
public class PaymentMethod extends IdentifiedValueObject {
private String cardNumber;
private String cardHolderName;
private ExpirationDate expirationDate;
private String securityNumber;
Figure: copy of the attributes in our PaymentMethod value object

ExpirationDate Value Object (domain – used by PaymentMethod)


public class ExpirationDate extends ValueObject {
private int expiryMonth;
private int expiryYear;
Figure: copy of the attributes in our ExpirationDate value object

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

protected long id(){


return id;
}
}
Figure: copy of the attributes in IdentifiedValueObject

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.

Start by reviewing the SQL for this:


CREATE TABLE payment_method(
id int AUTO_INCREMENT PRIMARY KEY,
card_number VARCHAR NOT NULL,
cardholder_name VARCHAR NOT NULL,
expiration_date DATE NOT NULL,
security_number VARCHAR NOT NULL,
buyer_id VARCHAR NOT NULL,
FOREIGN KEY(buyer_id) REFERENCES buyer(id)
);

11
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries

Sample Data for these tables (data.sql in resources)


• 2 payments for our buyer will be defined.
• Sequence to enable auto generation of id in Payment Method table (see
PaymentMethodJpaValueObject to see how this is utilised).

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');

create sequence payment_method_sequence_id start with (select max(id) + 1


from payment_method);

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")

private long id;

@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;

@Column(name="buyer_id")//Id field for buyer


private String buyer;

//Needed for DTO convertor


public PaymentMethodJpaValueObject(){}

12
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries

public PaymentMethodJpaValueObject(long id, String


cardholderName,String cardNumber,LocalDate expirationDate, String
securityNumber){
this.id = id; //needed to identify record when saving
this.cardNumber = cardNumber;
this.cardholderName = cardholderName;
this.expirationDate = expirationDate;
this.securityNumber = securityNumber;
}
}

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).

CrudBuyerRepository (CrudRepository interface for BuyerJpa)


@Repository
interface CrudBuyerRepository
extends CrudRepository<BuyerJpa, String> {

@Query("FROM buyer")
Iterable<IBuyerJpa> findAllBuyers();

Optional<IBuyerJpa> findBuyersById(String buyer_id);

@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;

public Iterable<IBuyerJpa> findAllBuyers() {


return repository.findAllBuyers();
}

public Iterable<IBuyerJpa> findPaymentsForBuyerById() {


return repository.findAllBuyers();
}

public Optional<IBuyerJpa> findBuyersById(String id){


return repository.findBuyersById(id);
}

IBuyerRepository (interface for BuyerRepository so we can use the repository in a different


layer via IOC)
When we compare this to CrudBuyerRepository we have the same methods – but this is a shareable
interface whereas the other one will create a proxy and contains direct reference to BuyerJpa it is
extension of CrudRepository.
public interface IBuyerRepository {

Iterable<IBuyerJpa> findAllBuyers();

Optional<IBuyerJpa> findBuyersById(String id);

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

Looking at the application by considering each layer when we make a


request

Data Transfer Object (DTO) – important information

“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).

Figure 1: Data Transfer Object Pattern (Fowler, 2002)

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

Figure 2: Microsoft explanation of where a DTO is used.

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

Comparing DTOs to Entities

Entity Data Transfer Object


Represent all data for a single table/entity Container for data transporting between layers
persisted to a database
Every instance represents a row in a table. Every instance contains ALL the data required by
the UI or to send to lower layers in the
application to be processed/stored.
Represents a single entity/table. Can contain some or all data that relates to an
entity – as required (no unnecessary data).

Can contain data from more than one entity (as


required by the UI).

Points in favour of DTOs

• 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.

Points against DTOs

• 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)

• End point for buyer aggregate starts with ‘/buyers’


e.g. localhost:8900/buyers

• 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

Query Handler method calls – findAll (application layer)

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.

Remember this controller handles end points starting with: @RequestMapping("/buyers")


@RequestMapping("/buyers")

The result of findAll is a set of Iterable objects – we can use <?> to avoid identifying a specific type
here.

getAllBuyersDetails (controller - UI layer)


@GetMapping("/findAll")
public Iterable<?> getAllBuyerDetails(){
return queryHandler.findAllBuyers();
}

findAll method in BuyerQueryHandler (application layer)


IBuyerQueryHandler has the same method signatures as BuyerQueryHandler
@AllArgsConstructor
@Service //Will not find this class without this annotation when trying to
inject
public class BuyerQueryHandler implements IBuyerQueryHandler {
private IBuyerRepository buyerRepository;

//Not recommended to have this method


public Iterable<IBuyerJpa> findAllBuyers(){
return buyerRepository.findAllBuyers();
}

findAll method in BuyerRepository (infrastructure layer)


IBuyerRepository – discussed earlier in this document.

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;

public Iterable<IBuyerJpa> findAllBuyers() {


return repository.findAllBuyers();
}

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.

Remember this controller handles end points starting with:


@RequestMapping("/buyers")

http://localhost:8900/buyers/0000

getBuyerById (controller)
Our value e.g. 0000 is passed as an argument after /buyers/

@GetMapping("/{buyer_id}")// set as optional /buyers/buyer_id - as may not


find id
public Optional<?> getBuyerById(@PathVariable(value="buyer_id")
String buyer_id){

Optional<?> result = queryHandler.findBuyerById(buyer_id);


if(result.isPresent()) {
return result;
}
else{
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "id not
found");
}
}
Continued on next page…

21
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries

findBuyerById method in BuyerQueryHandler (application layer)


The buyer id submitted may be unknown so we make the result Optional.

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){

Optional<IOrderJpa> result = orderRepository.findByBuyerId(buyer_id);


if (result.isPresent()) {
return convertOrderDetailsToDTO(result.get());
}
return Optional.empty();
}

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);
}

findById method in BuyerRepository (infrastructure layer)


public Optional<IBuyerJpa> findBuyersById(String id){
return repository.findBuyersById(id);
}

22
Phil James
Enterprise Application Development – Domain Driven Design – part 4 - Queries

Query Handler method calls – getPaymentDetailsForBuyer (payments associated with


a specific aggregate root)
Remember this controller handles end points starting with: @RequestMapping("/buyers")

If our buyer id was 0000 – then our request would be:


http://localhost:8900/buyers/0000
@GetMapping("/paymentDetails/{buyer_id}")
public List<?> getPaymentDetailsForBuyer(@PathVariable(value="buyer_id")
String buyer_id){
List<?> result = queryHandler.findPaymentsForBuyerById(buyer_id);

return result;
}

public List<PaymentMethodDTO> findPaymentsForBuyerById(String buyer_id){


Optional<IBuyerJpa> result = buyerRepository.findBuyersById(buyer_id);
if (result.isPresent()) {
return convertPaymentDetailsToDTO(result.get());
}
return new ArrayList<>();
}

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<>();

for (PaymentMethodJpaValueObject paymentMethod :


buyer.getPaymentMethods()){
paymentMethodDTOs.add(createPaymentMethodDTO(paymentMethod));
}

return paymentMethodDTOs;
}

private PaymentMethodDTO createPaymentMethodDTO(PaymentMethodJpaValueObject


paymentMethod){
ExpirationDate expirationDate = new
ExpirationDate(paymentMethod.getExpirationDate().getMonthValue(),
paymentMethod.getExpirationDate().getYear());

return new PaymentMethodDTO(paymentMethod.getCardNumber(),


paymentMethod.getCardholderName(),
expirationDate,
paymentMethod.getSecurityNumber());
}

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

You might also like