SlideShare a Scribd company logo
1 of 32
Download to read offline
Command-Query Separation
Architecture Pattern
Version: 0.2
Issue Date: January 2020
Copyright © 2020, by John Liebenau
Command-Query Separation Architecture Pattern
ii
Section Page
1 INTENT........................................................................................................................................................................... 1
2 ALSO KNOWN AS........................................................................................................................................................ 1
3 MOTIVATION............................................................................................................................................................... 1
4 APPLICABILITY .......................................................................................................................................................... 2
5 APPLICATION LEVEL................................................................................................................................................ 3
5.1 Structure..................................................................................................................................................................... 3
5.2 Participants................................................................................................................................................................. 3
5.3 Collaborations............................................................................................................................................................ 4
6 COMPONENT LEVEL................................................................................................................................................. 5
6.1 Structure..................................................................................................................................................................... 6
6.2 Participants................................................................................................................................................................. 6
7 MODULE LEVEL.......................................................................................................................................................... 7
7.1 Entity.CommandHost Modules.................................................................................................................................. 7
7.1.1 Structure................................................................................................................................................................ 8
7.1.2 Participants............................................................................................................................................................ 8
7.1.3 Collaborations..................................................................................................................................................... 10
7.2 Entity.UpdateHost Modules..................................................................................................................................... 10
7.2.1 Structure.............................................................................................................................................................. 11
7.2.2 Participants.......................................................................................................................................................... 12
7.2.3 Collaborations..................................................................................................................................................... 13
7.3 Entity.QueryHost Modules ...................................................................................................................................... 13
7.3.1 Structure.............................................................................................................................................................. 14
7.3.2 Participants.......................................................................................................................................................... 14
7.3.3 Collaborations..................................................................................................................................................... 15
8 CONSEQUENCES....................................................................................................................................................... 15
9 IMPLEMENTATION.................................................................................................................................................. 16
10 SAMPLE CODE........................................................................................................................................................... 29
11 KNOWN USES............................................................................................................................................................. 29
12 RELATED PATTERNS .............................................................................................................................................. 29
13 REFERENCES ............................................................................................................................................................. 29
Command-Query Separation Architecture Pattern
1 4/2/2020
1 Intent
Enable a service to be highly performant and horizontally scalable by separating its command interface/implementation
from its query interface/implementation so that commands and queries can scale independently of each other.
2 Also Known As
Command Query Responsibility Segregation
3 Motivation
Suppose you are implementing a service that manages a set of entities—the prices for products in an e-commerce system
for example. This service has a potentially large number of clients that will query the service to retrieve selected entities.
These “query” clients may also be distributed across multiple geographic regions. In contrast to the query use-case, the
service has a small number of clients that create, update, and delete the entities. These “command” clients are usually
concentrated in a single location. This service is critical to your business and must process requests and provide replies
consistently and quickly no matter the volume of requests at any given time and no matter the geographic region.
A simple seeming solution is to implement all operations: create, update, delete, and get in a single service that reads and
writes to a persistent datastore such as a relational database or a no-sql datastore. Instances of the service are then
horizontally scaled as needed. This approach is well suited to domains where there are a relatively small and evenly
distributed number of command clients and query clients, but the solution does not scale to a very large number of query
clients from multiple geographic regions. The single datastore becomes a bottleneck that cannot handle the load placed on
it by the vast number of query requests. Vertically scaling the datastore will enable some increased capacity but the query
request load will eventually overwhelm the datastore as more query clients make use of the service. In addition to query
requests slowing down dramatically as the number of requests increase, command requests will often be queued up waiting
to be processed which will cause the system’s entities to be out of date as well.
An alternative approach that sidesteps the bottleneck of a single datastore being overwhelmed by query requests is to
separate the service into multiple parts that handle different aspects of the supported functionality. The single service is
separated into:
• a Command Host – that provides a service for creating, updating, and deleting entities (i.e. create price, update price,
delete price) and emits events for each change in entity state (i.e. price created event, price updated event, price deleted
event) that contain a representation of the changed entity state
• an Update Host – that consumes entity events and writes the entity changes to a high speed datastore in a format that
is ready to be consumed for queries
• and a Query Host – that provides a service for querying entity data from the same high speed datastore that is updated
by the Update Host.
These separate host applications can be scaled independently of each other as needed and the Update and Query Hosts and
their shared high speed datastore can have multiple instances distributed across different geographic regions to enable
horizontal scaling for queries that is only limited by your operations budget.
A concrete example of this approach can be applied to our price service which is implemented as three kinds of applications:
• Price.CommandHost – an application that hosts the service functionality for creating, updating, and deleting product
prices and emits price events on each price state change (e.g. price created, price updated, and price deleted)
• Price.UpdateHost – an application that hosts the functionality of consuming price events and writing these state
changes to a high speed datastore in an optimized data format that does not require extensive (or any) transformation
when the data is retrieved when processing a query request
• Price.QueryHost – an application that hosts the service functionality for retrieving one or more prices (in an optimized
data format) from the high speed datastore that has been updated by Price.UpdateHost
Command-Query Separation Architecture Pattern
2 4/2/2020
A message topic:
• Price.Events – a message topic to which price events are sent by Price.CommandHost and from which price events are
received by Price.UpdateHosts
And two kinds of datastores:
• Price.CommandDatastore – a datastore that contains price entities in a format that is easy to manage price creation,
price updates, and price deletion
• Price.QueryDatastore – a high speed datastore that contains price (data) in a format that requires little or no
transformations when returned as the replies to query requests
The following diagram illustrates the application level components described above and their primary relationships.
4 Applicability
Use the Command-Query Separation pattern when:
• You have a service that has a small number of clients that make command requests (create, update, delete) and a very
large number of clients—possibly in different geographical regions—that make query requests.
• You must horizontally scale queries to meet internet level loads (i.e. hundreds of thousands of clients or greater, each
making multiple query requests).
• Query requests must execute consistently and quickly (in a specified time interval) no matter the overall number of
requests at any single point in time.
• You have a service that is tolerant of eventual consistency [1].
- Eventual consistency is a characteristic of distributed computing for achieving high availability that informally
guarantees that, if no new updates are made to a given data item, eventually all accesses to that item will return the
last updated value.
- This means that there is an acceptable window—the shorter the better—where a query request for specific entities
may return a value that is inconsistent with the value set by the latest command request on those entities. Ideally,
only a very small percentage of requests will fall into this inconsistency window.
Do not use the Command-Query Separation pattern when:
• You have a service that has strict transactional requirements (i.e. must conform to ACID constraints: atomic, consistent,
isolated, durable) [2]
Command-Query Separation Architecture Pattern
3 4/2/2020
5 Application Level
5.1 Structure
5.2 Participants
Participant Responsibilities
Entity.CommandHost (Price.CommandHost) • Exposes the EntityCommandService interface that provides create,
update, and delete operations
• Hosts the EntityCommandService implementation
• Sends EntityEvents to Entity.Events topic
• Maintains Entity state by creating, updating, and deleting Entities in
the Entity.CommandDatastore
Entity.UpdateHost (Price.UpdateHost) • Receives EntityEvents from Entity.Events topic
• Hosts the EntityEventProcessor that processes incoming
EntityEvents by creating, updating, and deleting EntityData in the
EntityData.QueryDatastore
Entity.QueryHost (Price.QueryHost) • Exposes the EntityQueryService interface that provides get operations
• Hosts the EntityQueryService implementation that retrieves
EntityData from the EntityData.QueryDatastore
Entity.Events (Price.Events) • Publish-subscribe channel for EntityEvents
Entity.CommandDatastore (Price.CommandDatastore) • Storage for Entities that are created and updated by
Entity.CommandHost
• Contains Entities in a persistent format
Entity.QueryDatastore (Price.QueryDatastore) • High speed storage for EntityData that are created and updated by
Entity.UpdateHost and retrieved by Entity.QueryHost
• Contains EntityData in a format optimized for retrieval and return to
clients
Command-Query Separation Architecture Pattern
4 4/2/2020
5.3 Collaborations
The Command-Query Separation pattern has the following collaborations:
• Creating Entities
- A command-client sends a CreateEntityRequest to the Entity.CommandHost through the
EntityCommandService’s createEntity(request) API
- The Entity.CommandHost processes the request and inserts the newly created Entity into the
Entity.CommandDatastore
- The Entity.CommandHost creates an EntityEvent (of event type CREATED and containing an EntityData
transfer object mapped from the source Entity) and sends it to the Entity.Events topic
- The Entity.UpdateHost receive the EntityEvent containing the created EntityData transfer object and inserts it
into the Entity.QueryDatastore
• Updating Entities
- A command-client sends an UpdateEntityRequest to the Entity.CommandHost through the
EntityCommandService’s updateEntity(request) API
- The Entity.CommandHost processes the request and updates the modified Entity in the
Entity.CommandDatastore
- The Entity.CommandHost creates an EntityEvent (of event type Updated and containing an EntityData transfer
Command-Query Separation Architecture Pattern
5 4/2/2020
object mapped from the source Entity) and sends it to the Entity.Events topic
- The Entity.UpdateHost receive the EntityEvent containing the modified EntityData transfer object and updates
it in the Entity.QueryDatastore
• Getting Entities
- A query-client sends a GetEntityRequest or GetEntitiesRequest to the Entity.QueryHost through the
EntityQueryService’s getEntity(request) or getEntities(request) APIs.
- The Entity.QueryHost processes the request by retrieving one or more EntityData objects from the
Entity.QueryDatastore and returning them to the query-client
• Deleting Entities
- A command-client sends a DeleteEntityRequest to the Entity.CommandHost through the
EntityCommandService’s deleteEntity(request) API
- The Entity.CommandHost processes the request and deletes the specified Entity from the
Entity.CommandDatastore
- The Entity.CommandHost creates an EntityEvent (of event type DELETED and containing an EntityData
transfer object mapped from the source Entity) and sends it to the Entity.Events topic
- The Entity.UpdateHost receive the EntityEvent containing the deleted EntityData transfer object and deletes its
instance from the Entity.QueryDatastore
6 Component Level
Applications are composed by linking in components providing various elements of functionality:
• Entity.CommandHost application links in the following components:
- Entity.CommandService
- Entity.CommandServer
- Entity.Transfer
• Entity.UpdateHost application links in the following components:
- Entity.CommandService
- Entity.CommandClient
- Entity.QueryServer
- Entity.Transfer
• Entity.QueryHost application links in the following components:
- Entity.QueryService
- Entity.QueryServer
- Entity.Transfer
Command-Query Separation Architecture Pattern
6 4/2/2020
6.1 Structure
6.2 Participants
Participant Responsibilities
Entity.CommandService (Price.CommandService) • Contains modules providing service interfaces for Entity-related events and
command type requests and replies (create, update, delete)
Entity.CommandClient (Price.CommandClient) • Contains modules that implement the service interfaces and abstract classes
declared in Entity.CommandService as client-side proxies [3] that can be
used to the call the services implemented in Entity.CommandServer or to
receive events emitted from the Entity.CommandHost application
Entity.CommandServer (Price.CommandServer) • Contains modules that implement the interfaces declared in
Entity.CommandService with the necessary business logic and other
mechanisms such as internal application entities, repositories, and domain
events to fully realize the Entity.CommandService microservice
Entity.QueryService (Price.QueryService) • Contains modules providing service interfaces for Entity-related query type
requests and replies (get)
Entity.QueryClient (Price.QueryClient) • Contains modules that implement the service interfaces and abstract classes
declared in Entity.QueryService as client-side proxies [3] that can be used
to the call the services implemented in Entity.QueryServer
Entity.QueryServer (Price.QueryServer) • Contains modules that implement the interfaces declared in
Entity.QueryService with the necessary business logic and other
mechanisms such as internal application entities, repositories, and domain
events to fully realize the Entity.QueryService microservice
Entity.Transfer (Price.Transfer) • Contains modules that implement the shared transfer objects [4] used to
represent entities in requests, replies, and events.
Command-Query Separation Architecture Pattern
7 4/2/2020
7 Module Level
The Entity.CommandHost, Entity.UpdateHost, and Entity.QueryHost applications defined by the Command-Query
Separation pattern are organized into layered architectures by applying the Layers architecture pattern [5] [6]. The specific
layers of these applications are:
• Client or Presentation Layer (implied) – consumer of the services and data provided by the Service Layer.
• Service Layer – provides the contracts that represent the services offered by the system.
• Application Layer – implements the contracts from the Service Layer by coordinating application activity of elements
from the Service Layer and the Domain Layer.
• Domain Layer – contains information about the domain. This is the heart of the business software. The state of
business objects is held here. Persistence of the business objects and possibly their state is delegated to the Platform
Layer.
• Platform Layer – Provides a supporting library for all the other layers.
- On server side – provides communication between layers, implements persistence for business objects, integration
mechanisms such as HTTP invokers that receiving incoming requests and invoker the appropriate methods on
service implementations.
- On client-side – implements the contract from the Service Layer as proxies for client side use and provides
supporting libraries for Presentation Layer
A layer is represented by a component or one or more modules in a component. These components and modules are
composed into larger networks of functionality. Modules are connected through interfaces to reduce coupling. Each module
exports a set of provided interfaces and imports a set of required interfaces. The benefit of the modular architecture is that
these components can be assembled into multiple configurations increasing software reuse and because the components are
highly decoupled it is much easier to test them independently.
7.1 Entity.CommandHost Modules
The following table lists the components of the Entity.CommandHost application and their constituent modules:
Component Module Responsibilities
Entity.CommandService RequestReply • Represents the “Service” layer of the Command Host application
• Provides the EntityCommandService interface
• Provides request/reply transfer objects [4] used in EntityCommandService
- CreateEntityRequest/CreateEntityReply
- UpdateEntityRequest/UpdateEntityReply
- DeleteEntityRequest/DeleteEntityReply
Event • Provides the EntityEventSender interface [7]
Entity.Transfer Shared • Provides the EntityData transfer object used for transferring the Entity’s data
Event • Provides the EntityEvent transfer object that represents entity events
Entity.CommandServer Application • Represents the “Application” layer of the Command Host application
• Provides the EntityCommandServiceImpl class
• Provides the Mapper interface [8]
Domain • Represents the “Domain” layer of the Command Host application
• Provides the Entity domain class [9]
Command-Query Separation Architecture Pattern
8 4/2/2020
• Provides the EntityRepository interface [10] [11]
• Provides the EntityRepositoryImpl class
Platform • Represents the “Platform” layer of the Command Host application
• Provides the EntityCommandServiceEndpoint class
• Provides the PlatformSpecificMapper class [8]
• Provides the PlatformSpecificEntityEventSender class [7]
7.1.1 Structure
7.1.2 Participants
«Module» Entity.CommandService.RequestReply
Participant Responsibilities
EntityCommandService (PriceCommandService) • Declares methods for creating, updating, and deleting Entities
CreateEntityRequest (CreatePriceRequest) • Transfer Object that represents a request to create an Entity
CreateEntityReply (CreatePriceReply) • Transfer Object that represents a reply to an associated request to create an
Entity
Command-Query Separation Architecture Pattern
9 4/2/2020
UpdateEntityRequest (UpdatePriceRequest) • Transfer Object that represents a request to update an Entity
UpdateEntityReply (UpdatePriceReply) • Transfer Object that represents a reply to an associated request to update an
Entity
DeleteEntityRequest (DeletePriceRequest) • Transfer Object that represents a request to delete an Entity
DeleteEntityReply (DeletePriceReply) • Transfer Object that represents a reply to an associated request to delete an
Entity
«Module» Entity.CommandService.Event
Participant Responsibilities
EntityEventSender (PriceEventSender) • Declares methods for sending EntityEvent transfer objects.
«Module» Entity.Transfer.Shared
Participant Responsibilities
EntityData (PriceData) • Transfer Object that contains the transferrable data of an Entity
«Module» Entity.Transfer.Event
Participant Responsibilities
EntityEvent (PriceEvent) • Transfer Object that represents the types of events that can occur on an Entity
• Contains the EntityData that was the source of the event
«Module» Entity.CommandServer.Application
Participant Responsibilities
EntityCommandServiceImpl
(PriceCommandServiceImpl)
• Server-side implementation of the PriceCommandService interface
• Implements the method for creating, updating, and deleting Entities
• Emits EntityEvents that correspond to each operation (create, update, delete) on Entities
Mapper (PriceMapper) • Declares an interface for mapping Entities and related objects to and from their Transfer
Object equivalents
«Module» Entity.CommandServer.Domain
Participant Responsibilities
Command-Query Separation Architecture Pattern
10 4/2/2020
Entity (Price) • Represents a specific domain concept
• Has a unique identity
• Transitions through multiple states
EntityRepository (PriceRepository) • Declares methods for inserting, updating, deleting, and retrieving Entities to/from a
storage mechanism
EntityRepositoryImpl
(PriceRepositoryImpl)
• Provides a storage specific implementation of the EntityRepository interface
«Module» Entity.CommandServer.Platform
Participant Responsibilities
EntityCommandServiceEndpoint
(PriceCommandServiceEndpoint)
• Platform specific (e.g. HTTP/REST) endpoint for the service functionality declared in the
EntityCommandService interface
• Exposes EntityCommandService operations to clients
PlatformSpecificMapper
(ModelMapper)
• Platform specific implementation of the Mapper interface
PlatformSpecificEntityEventSender
(KafkaAvroPriceEventSender)
• Platform specific implementation of the EntityEventSender interface declared in the
Entity.CommandService.Event module
7.1.3 Collaborations
• EntityCommandServiceEndpoint receives platform specific requests, converts them to an application usable format
such as Create | Update | Delete EntityRequest, and forwards them to an implementation of the
EntityCommandService interface for processing
• EntityCommandServiceImpl processes an incoming Create|Update|Delete EntityRequest by
- inserting, updating, or deleting the Entity object specified in the request to/from the Entity.CommandDatastore
through the EntityRepository
- using a Mapper to map the Entity object to an EntityData transfer object
- creating an appropriate EntityEvent transfer event object with a mapped EntityData as the event source
- sending the EntityEvent to the Entity.Events topic using the EntityEventSender
7.2 Entity.UpdateHost Modules
The following table lists the components of the Entity.UpdateHost application and their constituent modules:
Component Module Responsibilities
Entity.CommandService Event • Represents the “Service” layer of the Update Host application
• Provides the EntityEventReceiver interface
• Provides the EntityEventListener interface
Entity.Transfer Shared • Provides the EntityData transfer object used for transferring the Entity’s data
Command-Query Separation Architecture Pattern
11 4/2/2020
Event • Provides the EntityEvent transfer object that represents entity events
Entity.QueryServer Application • Represents the “Application” layer of the Update Host application
• Provides the EntityEventListenerImpl class
• Provides the Mapper interface
Domain • Represents the “Domain” layer of the Update Host application
• Provides the EntityDataRepository interface
Platform • Represents the “Platform” layer of the Update Host application
• Provides the PlatformSpecificEntityDataRepositoryImpl class
• Provides the PlatformSpecificEntityEventReceiver class
7.2.1 Structure
Command-Query Separation Architecture Pattern
12 4/2/2020
7.2.2 Participants
«Module» Entity.CommandService.Event
Participant Responsibilities
EntityEventReceiver (PriceEventReceiver) • Declares methods for starting/stopping receiving EntityEvent transfer objects
• Declares methods for attaching/detaching an EntityEventListener to listen for
EntityEvents
EntityEventListener (PriceEventListener) • Declares an onEvent(EntityEvent) method to be called on receiving an incoming
EntityEvent
• Declares an onException(Exception) method to be called on encountering an
exception while receiving an incoming EntityEvent
«Module» Entity.Transfer.Shared
Participant Responsibilities
EntityData (PriceData) • Transfer Object that contains the transferrable data of an Entity
«Module» Entity.Transfer.Event
Participant Responsibilities
EntityEvent (PriceEvent) • Transfer Object that represents the types of events that can occur on an Entity
• Contains the EntityData that was the source of the event
«Module» Entity.QueryServer.Application
Participant Responsibilities
EntityEventListenerImpl
(PriceEventListenerImpl)
• Implements EntityEventListener interface
• Creates, updates, and deletes EntityData objects using EntityDataRepository
«Module» Entity. QueryServer.Domain
Participant Responsibilities
EntityDataRepository (PriceDataRepository) • Declares methods for inserting, updating, deleting, and retrieving EntityData
objects to/from a storage mechanism
«Module» Entity. QueryServer.Platform
Command-Query Separation Architecture Pattern
13 4/2/2020
Participant Responsibilities
PlatformSpecificEntityDataRepositoryImpl
(RedisPriceDataRepository)
• High speed storage specific implementation of the EntityDataRepository
interface
PlatformSpecificEntityEventReceiver
(KafkaAvroPriceEventReceiver)
• Platform specific implementation of the EntityEventReceiver interface declared
in the Entity.CommandService.Event module
7.2.3 Collaborations
TBD
7.3 Entity.QueryHost Modules
The following table lists the components of the Entity.QueryHost application and their constituent modules:
Component Module Responsibilities
Entity.QueryService RequestReply • Represents the “Service” layer of the Query Host application
• Provides the EntityQueryService interface
• Provides request/reply transfer objects [4] used in EntityQueryService
- GetEntityRequest/GetEntityReply
- GetEntitiesRequest/GetEntitiesReply
Entity.Transfer Shared • Provides the EntityData transfer object used for transferring the Entity’s data
Entity.QueryServer Application • Represents the “Application” layer of the Query Host application
• Provides the EntityQueryServiceImpl class
Domain • Represents the “Domain” layer of the Query Host application
• Provides the EntityDataRepository interface
Platform • Represents the “Platform” layer of the Query Host application
• Provides the PlatformSpecificEntityDataRepositoryImpl class
• Provides the EntityQueryServiceEndpoint class
Command-Query Separation Architecture Pattern
14 4/2/2020
7.3.1 Structure
7.3.2 Participants
«Module» Entity.QueryService.RequestReply
Participant Responsibilities
EntityQueryService (PriceQueryService) • Declares methods for retrieving EntityData
GetEntityRequest (GetPriceRequest) • Transfer Object that represents a request to retrieve a single EntityData
GetEntityReply (GetPriceReply) • Transfer Object that represents a reply to an associated request to retrieve a
single EntityData
GetEntitiesRequest (GetPricesRequest) • Transfer Object that represents a request to retrieve multiple EntityData
Command-Query Separation Architecture Pattern
15 4/2/2020
GetEntitiesReply (GetPricesReply) • Transfer Object that represents a reply to an associated request to retrieve
multiple EntityData
«Module» Entity.Transfer.Shared
Participant Responsibilities
EntityData (PriceData) • Transfer Object that contains the transferrable data of an Entity
«Module» Entity.QueryServer.Application
Participant Responsibilities
EntityQueryServiceImpl
(PriceQueryServiceImpl)
• Server-side implementation of the EntityQueryService interface
• Implements the methods for retrieving EntityData
«Module» Entity. QueryServer.Domain
Participant Responsibilities
EntityDataRepository (PriceDataRepository) • Declares methods for inserting, updating, deleting, and retrieving EntityData
objects to/from a storage mechanism
«Module» Entity. QueryServer.Platform
Participant Responsibilities
PlatformSpecificEntityDataRepositoryImpl
(RedisPriceDataRepository)
• High speed storage specific implementation of the EntityDataRepository
interface
EntityQueryServiceEndpoint
(PriceQueryServiceEndpoint)
• Platform specific (e.g. HTTP/REST) endpoint for the service functionality
declared in the EntityQueryService interface
• Exposes EntityQueryService operations to clients
7.3.3 Collaborations
TBD
8 Consequences
Benefits
• High Performance Queries –
Command-Query Separation Architecture Pattern
16 4/2/2020
• Robust Horizontal Scalability –
• Geographical Distribution –
Liabilities
• Inconsistency Window –
• Introduces Complexity –
Trade-offs
• Eventual Consistency vs High Performance Queries & Horizontal Scalability –
9 Implementation
The following implementation issues should be considered when implementing the Command-Query Separation pattern:
• Multi-Component Workspace Organization – The development workspace for a system following the Command-
Query Separation pattern will be composed of multiple applications and multiple components. A suggested way of
organizing this workspace is to group the code and resources for applications, components, and tests into separate
directories that clearly indicate the purpose of each element:
- Applications
 The executable artifacts that host the system’s functionality
 Applications are constructed by importing the required Components and wiring them together using a
dependency injection mechanism
 A system has one or more applications
- Components
 The composable artifacts that provide interfaces and implementations that make up the system’s microservices
and other mechanisms
 A typical microservice is implemented with three Components:
▪ A service component that defines the contract of the microservice
▪ A server component that implements the business logic of the microservice
▪ A client component that provides proxies for consuming the microservice
- Tests
 The executable artifacts that verify the operational semantics of the elements defined in the system’s
Components
 Tests should be separate from Components
 Tests can be mapped to Components in many ways (the example below follows a one-to-one mapping)
The directory structure of such a system would look like the following:
 Applications/
 Price.CommandHost/
Command-Query Separation Architecture Pattern
17 4/2/2020
 Price.UpdateHost/
 Price.QueryHost/
 Components/
 Price.CommandService/
 Price.CommandClient/
 Price.CommandServer/
 Price.QueryService/
 Price.QueryClient/
 Price.QueryServer/
 Price.Transfer/
 Tests/
 Price.CommandServiceTest/
 Price.CommandClientTest/
 Price.CommandServerTest/
 • • •
This structure can be realized in many popular development environments. If you are implementing on a JVM platform you can
use an IDE such as IntelliJ IDEA. In IDEA, workspaces are called projects and elements such as applications, components, and
tests are called modules. IDEA enables the creation of multi-module projects that correspond to the above suggested directory
structure (see figure below).
Command-Query Separation Architecture Pattern
18 4/2/2020
If you are implementing on the .Net platform you can use the Visual Studio IDE. In Visual Studio, workspaces are called
solutions and applications, components, and tests are called projects. Visual Studio enables the creation of multi-project solutions
that correspond to the suggested directory structure (see figure below).
Command-Query Separation Architecture Pattern
19 4/2/2020
Dependency management and build tools such as Maven also can be configured to support multi-component workspaces. Maven
follows the same naming convention as IntelliJ IDEA (or rather IDEA follows Maven’s naming convention). Workspaces are
called projects and applications, components, and tests are called modules. Below is a multi-module Maven POM file that
supports our suggested directory structure:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>nrhl</groupId>
<artifactId>Cms.Price</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>Components/Cms.Price.Transfer</module>
<module>Components/Cms.Price.CommandService</module>
<module>Components/Cms.Price.CommandServer</module>
<module>Components/Cms.Price.CommandClient</module>
<module>Components/Cms.Price.QueryService</module>
<module>Components/Cms.Price.QueryServer</module>
<module>Components/Cms.Price.QueryClient</module>
<module>Tests/Cms.Price.TransferTest</module>
<module>Tests/Cms.Price.CommandServiceTest</module>
<module>Tests/Cms.Price.CommandServerTest</module>
<module>Tests/Cms.Price.CommandClientTest</module>
Command-Query Separation Architecture Pattern
20 4/2/2020
<module>Tests/Cms.Price.QueryServiceTest</module>
<module>Tests/Cms.Price.QueryServerTest</module>
<module>Tests/Cms.Price.QueryClientTest</module>
<module>Applications/Cms.Price.CommandHost</module>
<module>Applications/Cms.Price.UpdateHost</module>
<module>Applications/Cms.Price.QueryHost</module>
</modules>
</project>
Maven is often used inside of IntelliJ IDEA to configure dependency management and builds.
• Applications, Components, Tests, and Artifacts – Applications, components, and tests are logical elements that contain
source code and other resources. They are logical in the sense that they specify the parts of the system. During the
system build process, the source code and resources contained in the application, component, and test elements gets
compiled and packaged into artifacts. Artifacts are files that contain executable or interpretable code and are deployable
elements in the sense that they can be deployed to an execution environment and run in some fashion.
Application are executable artifacts in that they can be executed directly as in the case with .exe files or indirectly as in
the case *.jar files that contain a main entry point (a class with a static main method).
Components are composable artifacts in that they contain executable code, but they must be linked into an Application
to be executed. This is because components lack a main entry point to start application execution. On the .Net platform,
components are compiled and packaged into *.dll files while on the JVM platform components are compiled into *.jar
files.
Tests are essentially components but contain test code instead of application code.
• Modules and Dependency Injection – Each component in the system is further organized into one or more modules
that can be composed into larger networks of functionality. These modules are connected through interfaces to reduce
coupling. Each module exports a set of provided interfaces and imports a set of required interfaces. The benefit of the
modular architecture is that these components can be assembled into multiple configurations increasing software reuse
and because the components are highly decoupled it is much easier to test them independently [12].
Modules are also an important concept in dependency injection where they are a mechanism to define a set of related
dependency bindings. The dependency injection modules used by an application are loaded into the dependency
injection frameworks injector or container to wire together objects based on the bindings defined in the modules.
These two ideas—modules as logical groupings of functionality and modules as related dependency injection
bindings—align very well and is one of the main reasons that module-based dependency injection is a powerful tool for
organizing software. As mentioned above, logically, modules export a set of provided interfaces and import a set of
required interfaces. The following Java example illustrates how a module with provided and required interfaces are
specified using the Guice dependency injection framework [13].
The PriceApplicationModule is a class that extends the Guice AbstractModule base class. All Guice modules are
derived from AbstractModule and is the means by which the Guice Injector identifies modules.
public
class PriceApplicationModule
extends AbstractModule
{
@Override
protected void
configure()
{
bind(IPriceCommandService.class)
.to(PriceCommandService.class)
.in(getDefaultScope());
bind(ModelMapper.class)
Command-Query Separation Architecture Pattern
21 4/2/2020
.toProvider(
() ->
new ModelMapper()
.registerModule(new PriceMappingModule()))
.in(Scopes.SINGLETON);
. . .
}
}
PriceApplicationModule overrides the configure() method from AbstractModule to define the dependency injection
bindings of the module. In the above code example, the IPriceCommandService interface is being bound to the
PriceCommandService concrete class. IPriceCommandService is one of the provided interfaces of the module. The
required interfaces of the PriceApplicationModule are specified by the parameters of the PriceCommandService
constructor.
public
class PriceCommandService
extends AbstractService
implements IPriceCommandService, IPriceDomainEventObserver
{
private final IPriceRepository itsRepository;
private final IPriceEventSender itsSender;
private final ModelMapper itsMapper;
@Inject
public
PriceCommandService(
IUnitOfWorkProvider p,
IActionQueue q,
IPriceRepository r,
IPriceEventSender s,
ModelMapper m)
{
super(p,q);
itsRepository = r;
itsSender = s;
itsMapper = m;
}
. . .
}
The PriceCommandService constructor has the following parameters types:
- IUnitOfWorkProvider
- IActionQueue
- IPriceRepository
- IPriceEventSender
- ModelMapper
These types—excluding ModelMapper—are some of the required interfaces of the PriceApplicationModule and are the
provided interfaces of other modules that the PriceApplicationModule depends on. ModelMapper is not a required interface
because its binding is defined in PriceApplicationModule and is technically a provided interface that is injected into
PriceCommandService. The following diagram illustrates these dependencies.
Command-Query Separation Architecture Pattern
22 4/2/2020
PriceDomainModule and PricePlatformModule also extend AbstractModule and override configure() with
dependency injection bindings for their respective provided interfaces.
Modules are also a means of introducing a layered architecture to the system [6] [5]. The PriceApplicationModule,
PriceDomainModule, and PricePlatformModule correspond respectively to the Application, Domain, and Platform
layers of the Price.CommandHost application (see section 7 Module Level).
• High Performance Event Processing – One of the trade-offs of using the Command-Query Separation pattern is that
the solution is inherently eventually consistent so it can support high performance and horizontal scalability. Eventual
consistency means that there will be a window of time between the point that the Command Host commits a create,
update, or delete operation to the command datastore and the point that the Update Host receives the event associated
with that operation and updates the query datastore with the new state. We want to make that window as small as
possible to avoid inconsistency between the command state and the query state. A key element in reducing the
inconsistency window is to optimize the event processing between the command host and the update host.
There are multiple things that can be done to optimize event processing:
- Select a high performance messaging or streaming platform such as Apache Kafka [14]. Kafka is a distributed data
store optimized for ingesting and processing streaming data in real-time. The primary elements in Kafka are:
Command-Query Separation Architecture Pattern
23 4/2/2020
 Topics and Partitions – A topic is a named stream to which records are published. Topics can have zero, one,
or many consumers that subscribe to the data written to it. Each topic stores its data in a partitioned log managed
by the Kafka cluster:
Each partition is an ordered, immutable sequence of records that is continually appended to—a structured
commit log. The records in the partitions are each assigned a sequential id number called the offset that uniquely
identifies each record within the partition. Partitions are the unit of concurrency in Kafka.
 Producers – A producer publishes data to a topic by appending data records to the partitions contained in the
topic.
 Consumers and Consumer Groups – A consumer subscribes to a topic in order to consume data that has been
published to the topic by producers. Each consumer is a member of a consumer group which is a mechanism
that enables the topic to deliver a published record to one and only one consumer in a specific consumer group.
If multiple consumer instances are part of the same consumer group, then records published on the topic will
effectively be load balanced over the consumer instances. If multiple consumer instances have different
consumer groups, then each record published on the topic will be broadcast to each of the consumer instances.
- Assign a consumer group for each region. Each geographical region will contain one or more consumer groups to
enable a broadcast of EntityEvents to Entity.UpdateHosts in all regions.
- Partition Kafka topics to maximize throughput. The Entity.Events topic should be created with at least the same
number of partitions as the maximum number of consumers instance in the consumers groups. Let N be number of
partitions for the Entity.Events topics and {C1, C2, …, Ci} be the set of consumers in each of the system’s consumer
groups, then
𝐍 ≥ 𝑚𝑎𝑥(𝐶1, 𝐶2, … , 𝐶𝑖)
- Ensure that entity events are delivered in order. When Kafka topics have more than one partition, producers must
determine which partition to write the event. Kafka provides a mechanism for controlling which partition is written
to by using a key during event publishing. The producer selects the partition by calculating the modulo of the key’s
hash code to the number of partitions in the topic:
𝑝𝑎𝑟𝑡𝑖𝑡𝑖𝑜𝑛𝐼𝑛𝑑𝑒𝑥 = 𝑘𝑒𝑦. 𝑔𝑒𝑡𝐻𝑎𝑠ℎ𝐶𝑜𝑑𝑒() 𝒎𝒐𝒅 𝑛𝑢𝑚𝑏𝑒𝑟𝑂𝑓𝑃𝑎𝑟𝑡𝑖𝑡𝑖𝑜𝑛𝑠
EntityEvent transfer objects contain an EntityData that is the source of the event:
If we use the EntityData’s entityId property as the key when publishing the EntityEvent to the topic, the producer
Command-Query Separation Architecture Pattern
24 4/2/2020
will write the events generated by a specific Entity to the same partition in the topic, thus ensuring that these events
will be delivered in order. The key can be set during ProducerRecord construction:
String topic = "Price.Events";
PriceEvent event = new PriceEvent().setSource(new PriceData(...));
String key = event.getSource().getPriceId();
ProducerRecord<String,PriceEvent> record = new ProducerRecord<>(topic,key,event);
- Use Apache Avro [15] to generate transfer objects and for serialization/deserialization. Kafka offers both string-
based and binary-based options for message serialization/deserialization. String-based serialization/deserialization
is convenient because it generally produces human readable data that makes it easier for engineers to read and
debug. But string-based serialization/deserialization is slower than binary-based mechanisms and the size of string-
based messages is much larger that the size of binary-based messages of the same data. Apache Avro is a binary-
based serialization/deserialization implementation that is well integrated with Kafka. In Avro, transfer objects are
defined using Avro Schema files. The following are the Avro Schema definitions of PriceEvent and related types:
{
"type" : "record",
"namespace" : "cms.price.transfer.event",
"name" : "PriceEvent",
"fields" : [
{"name" : "identifiers", "type": "strata.foundation.core.event.EventIdentifiersData"},
{"name" : "eventType", "type": "strata.foundation.core.event.StandardEventType"},
{"name" : "source", "type": "cms.price.transfer.shared.PriceData"},
{"name" : "exception", "type": ["null","strata.foundation.core.event.ExceptionData"], "default" : null}
]
}
{
"type" : "record",
"namespace" : "cms.price.transfer.shared",
"name" : "PriceData",
"fields" : [
{"name": "priceId", "type": ["null","long"], "default": null},
{"name": "productId", "type": ["null","long"], "default": null},
{"name": "currencyCode", "type": "string"},
{"name": "msrp", "type": "double"},
{"name": "regularPrice", "type": "double"},
{"name": "discounts", "type": {"type": "array", "items": "cms.price.transfer.shared.DiscountData"}}
]
}
{
"type" : "record",
"namespace" : "cms.price.transfer.shared",
"name" : "DiscountData",
"fields" : [
{"name": "discountName", "type": "string"},
{"name": "price", "type": "double"},
{"name": "percentage", "type": "double"}
]
}
These schema definitions are used to generate language-specific classes that are used by the application and clients
to produce and consume PriceEvents.
• Object to Object Mapping – Implementations following the Command-Query Separation pattern often use the Transfer
Object pattern [4] when building the request and reply objects that are inputs and outputs of the EntityCommandService
and the EntityQueryService interfaces. Transfer Objects in turn rely on the Mapper pattern [8] to map Transfer Objects
to and from Entities [4]. These mappers do not need to be built from scratch. There are several excellent object mapper
frameworks that do much of the work already. The Model Mapper framework [16] is available on the JVM platform
and the AutoMapper framework [17] is available on the .Net platform. Both frameworks use a convention-based
mapping strategy that automatically maps properties in source and destination object that have the same names and
types. For more complex mappings that are not supported by the default conventions, the frameworks provide fluent
interfaces for configuring the mappers as needed. The following is an example of configuring a Model Mapper mapping
Command-Query Separation Architecture Pattern
25 4/2/2020
module for the Price.CommandServer component in Java:
public
class PriceMappingModule
implements Module
{
@Override
public void
setupModule(ModelMapper mapper)
{
mapper
.createTypeMap(PriceData.class,IPrice.class)
.setProvider(request -> new StandardPrice())
.addMapping(PriceData::getPriceId,IPrice::setPrimaryId)
.addMappings(
mapping ->
mapping
.using(new DiscountDataListToDiscountMapConverter())
.map(PriceData::getDiscounts,IPrice::setDiscounts));
mapper
.createTypeMap(IPrice.class,PriceData.class)
.include(StandardPrice.class,PriceData.class)
.addMapping(IPrice::getPrimaryId,PriceData::setPriceId)
.addMappings(
mapping ->
mapping
.using(new DiscountMapToDiscountDataListConverter())
.map(IPrice::getDiscounts,PriceData::setDiscounts));
. . .
}
}
In the above code example, the PriceMappingModule is a class that defines all of the object-to-object mapping used
in the Price.CommandServer component. PriceMappingModule extends Model Mapper framework’s Module class
which is the base class of all mapping modules in Model Mapper. In the first mapping configuration, a mapping
relationship is created between the PriceData class (a transfer object) and the IPrice interface (an entity). Because
IPrice is an interface the configuration specifies the concreate StandardPrice class that implements IPrice with the
setProvider() method. Most of the properties in PriceData and IPrice have the same names and types so the
convention-based mappings are automatically used. The exception to this is the PriceId property in PriceData and the
PrimaryId property in IPrice. The PriceId property identifies the price object and is mapped to the PrimaryId property.
Another mismatch between the PriceData transfer object and the IPrice entity is that the Discounts property is
represented by different collection types. In PriceData, the Discounts property is a list of DiscountData objects. In
IPrice, the Discounts property is a map of Discount objects. The DiscountDataListToDiscountMapConverter class
is a converter that facilitates the conversion between the list and map types. In the second mapping configuration, the
opposite mapping relationship is created where IPrice is the source and PriceData is the destination.
• Repositories – The domain layers in both the Entity.CommandServer component and the Entity.QueryServer
components can be implemented using the Repository pattern [10] [11]. In the CommandServer component, the
EntityRepository is implemented with a relational database and an object-to-relational mapping framework such as
Hibernate. In the QueryServer component, the EntityDataRepository is implemented with a high speed key-value
store or a document store and some type of serialization/deserialization between the EntityData type and a string-based
format such as JSON.
• Key-Value Store vs Document Store – The following criteria is useful when deciding on what kind of high speed
datastore is needed for the query side of the system:
- If the domain of the service only requires simple key-based queries such as retrieving an object with a key or a set
of objects with multiple keys, then consider using a key-value datastore such as Redis.
Command-Query Separation Architecture Pattern
26 4/2/2020
- If the domain of the service requires more complex queries that select objects based on properties other than a
simple key, then consider using a document store such as Couchbase or Mongo.
• Service Endpoints – Services are exposed to clients through service endpoint classes. These classes are usually part of
a REST or Web Service framework and provide annotations (JVM platform) or attributes (.Net platform) that specify
the paths or routes, http request type (POST, PUT, GET, DELETE), and request and response content. The service
endpoint class can also use annotations from an api specification framework such as Open API [18] or Swagger [19]
to generate API documentation and executable API proxies for a variety of client platforms. The following is a code
example showing the PriceQueryServiceEndpoint class that uses the JAX-RS framework [20] to define the service
endpoint and Open API to generate API documentation and proxies:
@Path("price-query")
@Tag(
name = "PriceQueryService",
description =
"Service that provides create, update, delete, activate, " +
"and deactivate operations for prices.")
public
class PriceQueryServiceEndpoint
{
private IPriceQueryService itsImplementation;
@Inject
public void
setImplementation(IPriceQueryService impl) { itsImplementation = impl;}
@Path("get-price/{requestedPriceId}")
@GET
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Gets an existing price")
@ApiResponse(
responseCode = "200",
description = "successful reply",
content =
@Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(implementation = GetPriceReply.class)))
@ApiResponse(
responseCode = "500",
description = "exception",
content =
@Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(implementation = GetPriceReply.class)))
public CompletionStage<GetPriceReply>
getPrice(
@PathParam("requestedPriceId") Long requestedPriceId,
@QueryParam("correlationId") UUID correlationId)
{
return
itsImplementation.getPrice(
(GetPriceRequest)
new GetPriceRequest()
.setRequestedPriceId(requestedPriceId)
.setCorrelationId(correlationId)
.setTimestamp(Instant.now()));
}
. . .
}
PriceQueryServiceEndpoint also uses dependency injection to inject an instance of the IPriceQueryService
interface that provides the actual implementation of the service endpoint. This approach keeps the business logic
defined in the class implementing IPriceQueryService separate from the platform specific details of
PriceQueryServiceEndpoint. This separation of concerns enables multiple service endpoints if needed or an easy
path for moving to a different endpoint framework if the need arises.
Command-Query Separation Architecture Pattern
27 4/2/2020
• Scaling Horizontally with Containers, Clusters, and Container Orchestration – In order to scale horizontally, each of
the applications in the system—Entity.CommandHost, Entity.UpdateHost, and Entity.QueryHost—are built to be
executed in a container environment such as Docker and the instances of these containers are deployed to one or more
clusters such as Amazon EC2 or Google Cloud Platform. The number of container instances are automatically scaled
by the cluster’s container orchestration mechanism such as Kubernetes (Amazon EKS or Google GKE) or Amazon
ECS. The following is an example Dockerfile that defines the Docker image of Price.UpdateHost.
FROM openjdk:8-jre-alpine
ENV DEPLOY_ENV docker-development
EXPOSE 8097
COPY target/Cms.Price.UpdateHost-1.0-SNAPSHOT-jar-with-dependencies.jar /cms-price-updatehost.jar
CMD ["/usr/bin/java", "-jar", "-Xmx1024m", "/cms-price-updatehost.jar"]
The following diagram illustrates a common deployment pattern for systems following the Command-Query
Separation pattern:
Command-Query Separation Architecture Pattern
28 4/2/2020
Command-Query Separation Architecture Pattern
29 4/2/2020
10 Sample Code
Sample code for the Price Service used in the motivation section’s example can be found at:
https://hautelook.sharepoint.com/:u:/s/catalogmanagement/ESrzh_sjOn5Ns1RXuF9ZtrQBBFFwkUWWXcTzQzYy4QpEfQ?e=q53Ak1
11 Known Uses
TBD
12 Related Patterns
• Layers – The Layers Pattern [6] [5]
• Transfer Object – The Transfer Object Pattern [4]
• Mapper – The Mapper Pattern [8]
• Proxy – The Proxy Pattern [3]
• Messaging Gateway – The Messaging Gateway Pattern [7]
• Publish-Subscribe Channel – The Publish-Subscribe Channel Pattern [21]
• Repository – The Repository Pattern [10] [11]
• Unit-Of-Work – The Unit-Of-Work Pattern [22]
• Service Assembly – The Service Assembly Pattern [TBD]
13 References
[1] Peter Bailis and Ali Ghodsi, "Eventual Consistency Today: Limitations, Extensions, and Beyond," Queue, vol. 11,
no. 3, March 2013. [Online]. https://dl.acm.org/doi/pdf/10.1145/2460276.2462076
[2] Wikipedia Community. wikipedia.org. [Online]. https://en.wikipedia.org/wiki/ACID
[3] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, "Proxy," in Design Patterns: Elements of
Reusable Object-Oriented Software.: Addison-Wesley, 1995, pp. 207-212.
[4] Martin Fowler, "Data Transfer Object," in Patterns of Enterprise Application Architecture.: Addison-Wesley
Professional, 2003, pp. 401-413.
[5] Eric Evans, "Layers," in Domain-Driven Design: Tackling Complexity in the Heart of Software.: Addison-Wesley,
2004, pp. 71-72.
[6] Frank Buschman, Regine Meunier, Hans Rohnert, Peter Sommerlad, and Michael Stal, "Layers," in Pattern-
Oriented Software Architecture: A System of Patterns.: John Wiley & Sons, 1996, pp. 31-33.
[7] Gregor Hohpe and Bobby Woolfe, "Messaging Gateway," in Enterprise Integration Patterns: Designing, Building,
and Deploying Messaging Solutions.: Addison-Wesley, 2004.
Command-Query Separation Architecture Pattern
30 4/2/2020
[8] Martin Fowler, "Mapper," in Patterns of Enterprise Application Architecture.: Addison-Wesley Professional, 2003,
pp. 473-474.
[9] Eric Evans, "Entities," in Domain-Driven Design: Tackling Complexity in the Heart of Software.: Addison-Wesley,
2004, pp. 90-99.
[10] Martin Fowler, "Repository," in Patterns of Enterprise Application Architecture.: Addison-Wesley Professional,
2003, pp. 322-327.
[11] Eric Evans, "Repository," in Domain-Driven Design: Tackling Complexity in the Heart of Software.: Addison-
Wesley, 2004, pp. 158-159.
[12] Eric Evans, "Modules," in Domain-Driven Design: Tackling Complexity in the Heart of Software.: Addison-Wesley,
2004, p. 111.
[13] Google. (2015) github.com/google/guice. [Online]. https://github.com/google/guice/wiki/Motivation
[14] Apache Software Foundation. (2017) kafka.apache.org. [Online]. https://kafka.apache.org/intro
[15] Apache Software Foundation. (2012) avro.apache.org. [Online]. https://avro.apache.org/docs/current/
[16] Jonathan Halterman. (2019) modelmapper.org. [Online]. http://modelmapper.org/getting-started
[17] Jimmy Bogard. (2017) automapper.readthedocs.io. [Online]. https://automapper.readthedocs.io/en/latest/Getting-
started.html
[18] Open API Initiative. (2018) spec.openapis.org. [Online]. http://spec.openapis.org/oas/v3.0.2
[19] Smart Bear, Inc. (2019) swagger.io. [Online]. https://swagger.io/docs
[20] JAX-RS Specification Committee. (2017) github.com/jax-rs. [Online]. https://github.com/jax-
rs/spec/blob/master/spec.pdf
[21] Gregor Hohpe and Bobby Woolfe, "Publish-Subscribe Channel," in Enterprise Integration Patterns: Designing,
Building, and Deploying Messaging Solutions.: Addison-Wesley, 2004, pp. 105-106.
[22] Martin Fowler, "Unit of Work," in Patterns of Enterprise Application Architecture.: Addison-Wesley Professional,
2003, pp. 184-194.

More Related Content

Similar to Command Query Separation: Architecture Pattern

ABC Online Direct store software architecture using SOA (Service Oriented Arc...
ABC Online Direct store software architecture using SOA (Service Oriented Arc...ABC Online Direct store software architecture using SOA (Service Oriented Arc...
ABC Online Direct store software architecture using SOA (Service Oriented Arc...
Akash Mhankale
 
Jisto_Whitepaper_Recapturing_Stranded_Resources
Jisto_Whitepaper_Recapturing_Stranded_ResourcesJisto_Whitepaper_Recapturing_Stranded_Resources
Jisto_Whitepaper_Recapturing_Stranded_Resources
Kevin Donovan
 
Microsoft Windows Azure - EBC Deck June 2010 Presentation
Microsoft Windows Azure -  EBC Deck June 2010 PresentationMicrosoft Windows Azure -  EBC Deck June 2010 Presentation
Microsoft Windows Azure - EBC Deck June 2010 Presentation
Microsoft Private Cloud
 
Project Proposal
Project ProposalProject Proposal
Project Proposal
Mike Wells
 
Cost to Serve of large scale Online Systems - final
Cost to Serve of large scale Online Systems - finalCost to Serve of large scale Online Systems - final
Cost to Serve of large scale Online Systems - final
Andrés Paz
 
CONSULTANTS ANALYSIS REPORT 1 Colorado Techn.docx
CONSULTANTS ANALYSIS REPORT  1 Colorado Techn.docxCONSULTANTS ANALYSIS REPORT  1 Colorado Techn.docx
CONSULTANTS ANALYSIS REPORT 1 Colorado Techn.docx
donnajames55
 

Similar to Command Query Separation: Architecture Pattern (20)

Parallels
ParallelsParallels
Parallels
 
Microsoft SQL Azure - Scaling Out with SQL Azure Whitepaper
Microsoft SQL Azure - Scaling Out with SQL Azure WhitepaperMicrosoft SQL Azure - Scaling Out with SQL Azure Whitepaper
Microsoft SQL Azure - Scaling Out with SQL Azure Whitepaper
 
Jamcracker Cloud Management Platform Updates: Devops Framework, Migration Pla...
Jamcracker Cloud Management Platform Updates: Devops Framework, Migration Pla...Jamcracker Cloud Management Platform Updates: Devops Framework, Migration Pla...
Jamcracker Cloud Management Platform Updates: Devops Framework, Migration Pla...
 
ABC Online Direct store software architecture using SOA (Service Oriented Arc...
ABC Online Direct store software architecture using SOA (Service Oriented Arc...ABC Online Direct store software architecture using SOA (Service Oriented Arc...
ABC Online Direct store software architecture using SOA (Service Oriented Arc...
 
Integration Approach for MES
Integration Approach for MESIntegration Approach for MES
Integration Approach for MES
 
Black Box
Black BoxBlack Box
Black Box
 
SharePoint 2010 Global Deployment
SharePoint 2010 Global DeploymentSharePoint 2010 Global Deployment
SharePoint 2010 Global Deployment
 
Case Study For Replication For PCMS
Case Study For Replication For PCMSCase Study For Replication For PCMS
Case Study For Replication For PCMS
 
Marlabs - Elastic scalability Azure v1 0
Marlabs - Elastic scalability Azure v1 0Marlabs - Elastic scalability Azure v1 0
Marlabs - Elastic scalability Azure v1 0
 
Jisto_Whitepaper_Recapturing_Stranded_Resources
Jisto_Whitepaper_Recapturing_Stranded_ResourcesJisto_Whitepaper_Recapturing_Stranded_Resources
Jisto_Whitepaper_Recapturing_Stranded_Resources
 
Enabling SQL Access to Data Lakes
Enabling SQL Access to Data LakesEnabling SQL Access to Data Lakes
Enabling SQL Access to Data Lakes
 
Whitepaper: Software Defined Data Center – An Implementation view - Happiest ...
Whitepaper: Software Defined Data Center – An Implementation view - Happiest ...Whitepaper: Software Defined Data Center – An Implementation view - Happiest ...
Whitepaper: Software Defined Data Center – An Implementation view - Happiest ...
 
Microsoft Windows Azure - EBC Deck June 2010 Presentation
Microsoft Windows Azure -  EBC Deck June 2010 PresentationMicrosoft Windows Azure -  EBC Deck June 2010 Presentation
Microsoft Windows Azure - EBC Deck June 2010 Presentation
 
tower-server.pdf
tower-server.pdftower-server.pdf
tower-server.pdf
 
Whitepaper: Moving Towards a Virtual Data Center - Happiest Minds
Whitepaper: Moving Towards a Virtual Data Center - Happiest MindsWhitepaper: Moving Towards a Virtual Data Center - Happiest Minds
Whitepaper: Moving Towards a Virtual Data Center - Happiest Minds
 
Project Proposal
Project ProposalProject Proposal
Project Proposal
 
Cost to Serve of large scale Online Systems - final
Cost to Serve of large scale Online Systems - finalCost to Serve of large scale Online Systems - final
Cost to Serve of large scale Online Systems - final
 
Cómo transformar los datos en análisis con los que tomar decisiones
Cómo transformar los datos en análisis con los que tomar decisionesCómo transformar los datos en análisis con los que tomar decisiones
Cómo transformar los datos en análisis con los que tomar decisiones
 
New features in Zen 5.3 end-to-end network analytics platform
New features in Zen 5.3 end-to-end network analytics platformNew features in Zen 5.3 end-to-end network analytics platform
New features in Zen 5.3 end-to-end network analytics platform
 
CONSULTANTS ANALYSIS REPORT 1 Colorado Techn.docx
CONSULTANTS ANALYSIS REPORT  1 Colorado Techn.docxCONSULTANTS ANALYSIS REPORT  1 Colorado Techn.docx
CONSULTANTS ANALYSIS REPORT 1 Colorado Techn.docx
 

Recently uploaded

AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
masabamasaba
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
masabamasaba
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
shinachiaurasa2
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
masabamasaba
 

Recently uploaded (20)

AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT  - Elevating Productivity in Today's Agile EnvironmentHarnessing ChatGPT  - Elevating Productivity in Today's Agile Environment
Harnessing ChatGPT - Elevating Productivity in Today's Agile Environment
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 

Command Query Separation: Architecture Pattern

  • 1. Command-Query Separation Architecture Pattern Version: 0.2 Issue Date: January 2020 Copyright © 2020, by John Liebenau
  • 2. Command-Query Separation Architecture Pattern ii Section Page 1 INTENT........................................................................................................................................................................... 1 2 ALSO KNOWN AS........................................................................................................................................................ 1 3 MOTIVATION............................................................................................................................................................... 1 4 APPLICABILITY .......................................................................................................................................................... 2 5 APPLICATION LEVEL................................................................................................................................................ 3 5.1 Structure..................................................................................................................................................................... 3 5.2 Participants................................................................................................................................................................. 3 5.3 Collaborations............................................................................................................................................................ 4 6 COMPONENT LEVEL................................................................................................................................................. 5 6.1 Structure..................................................................................................................................................................... 6 6.2 Participants................................................................................................................................................................. 6 7 MODULE LEVEL.......................................................................................................................................................... 7 7.1 Entity.CommandHost Modules.................................................................................................................................. 7 7.1.1 Structure................................................................................................................................................................ 8 7.1.2 Participants............................................................................................................................................................ 8 7.1.3 Collaborations..................................................................................................................................................... 10 7.2 Entity.UpdateHost Modules..................................................................................................................................... 10 7.2.1 Structure.............................................................................................................................................................. 11 7.2.2 Participants.......................................................................................................................................................... 12 7.2.3 Collaborations..................................................................................................................................................... 13 7.3 Entity.QueryHost Modules ...................................................................................................................................... 13 7.3.1 Structure.............................................................................................................................................................. 14 7.3.2 Participants.......................................................................................................................................................... 14 7.3.3 Collaborations..................................................................................................................................................... 15 8 CONSEQUENCES....................................................................................................................................................... 15 9 IMPLEMENTATION.................................................................................................................................................. 16 10 SAMPLE CODE........................................................................................................................................................... 29 11 KNOWN USES............................................................................................................................................................. 29 12 RELATED PATTERNS .............................................................................................................................................. 29 13 REFERENCES ............................................................................................................................................................. 29
  • 3. Command-Query Separation Architecture Pattern 1 4/2/2020 1 Intent Enable a service to be highly performant and horizontally scalable by separating its command interface/implementation from its query interface/implementation so that commands and queries can scale independently of each other. 2 Also Known As Command Query Responsibility Segregation 3 Motivation Suppose you are implementing a service that manages a set of entities—the prices for products in an e-commerce system for example. This service has a potentially large number of clients that will query the service to retrieve selected entities. These “query” clients may also be distributed across multiple geographic regions. In contrast to the query use-case, the service has a small number of clients that create, update, and delete the entities. These “command” clients are usually concentrated in a single location. This service is critical to your business and must process requests and provide replies consistently and quickly no matter the volume of requests at any given time and no matter the geographic region. A simple seeming solution is to implement all operations: create, update, delete, and get in a single service that reads and writes to a persistent datastore such as a relational database or a no-sql datastore. Instances of the service are then horizontally scaled as needed. This approach is well suited to domains where there are a relatively small and evenly distributed number of command clients and query clients, but the solution does not scale to a very large number of query clients from multiple geographic regions. The single datastore becomes a bottleneck that cannot handle the load placed on it by the vast number of query requests. Vertically scaling the datastore will enable some increased capacity but the query request load will eventually overwhelm the datastore as more query clients make use of the service. In addition to query requests slowing down dramatically as the number of requests increase, command requests will often be queued up waiting to be processed which will cause the system’s entities to be out of date as well. An alternative approach that sidesteps the bottleneck of a single datastore being overwhelmed by query requests is to separate the service into multiple parts that handle different aspects of the supported functionality. The single service is separated into: • a Command Host – that provides a service for creating, updating, and deleting entities (i.e. create price, update price, delete price) and emits events for each change in entity state (i.e. price created event, price updated event, price deleted event) that contain a representation of the changed entity state • an Update Host – that consumes entity events and writes the entity changes to a high speed datastore in a format that is ready to be consumed for queries • and a Query Host – that provides a service for querying entity data from the same high speed datastore that is updated by the Update Host. These separate host applications can be scaled independently of each other as needed and the Update and Query Hosts and their shared high speed datastore can have multiple instances distributed across different geographic regions to enable horizontal scaling for queries that is only limited by your operations budget. A concrete example of this approach can be applied to our price service which is implemented as three kinds of applications: • Price.CommandHost – an application that hosts the service functionality for creating, updating, and deleting product prices and emits price events on each price state change (e.g. price created, price updated, and price deleted) • Price.UpdateHost – an application that hosts the functionality of consuming price events and writing these state changes to a high speed datastore in an optimized data format that does not require extensive (or any) transformation when the data is retrieved when processing a query request • Price.QueryHost – an application that hosts the service functionality for retrieving one or more prices (in an optimized data format) from the high speed datastore that has been updated by Price.UpdateHost
  • 4. Command-Query Separation Architecture Pattern 2 4/2/2020 A message topic: • Price.Events – a message topic to which price events are sent by Price.CommandHost and from which price events are received by Price.UpdateHosts And two kinds of datastores: • Price.CommandDatastore – a datastore that contains price entities in a format that is easy to manage price creation, price updates, and price deletion • Price.QueryDatastore – a high speed datastore that contains price (data) in a format that requires little or no transformations when returned as the replies to query requests The following diagram illustrates the application level components described above and their primary relationships. 4 Applicability Use the Command-Query Separation pattern when: • You have a service that has a small number of clients that make command requests (create, update, delete) and a very large number of clients—possibly in different geographical regions—that make query requests. • You must horizontally scale queries to meet internet level loads (i.e. hundreds of thousands of clients or greater, each making multiple query requests). • Query requests must execute consistently and quickly (in a specified time interval) no matter the overall number of requests at any single point in time. • You have a service that is tolerant of eventual consistency [1]. - Eventual consistency is a characteristic of distributed computing for achieving high availability that informally guarantees that, if no new updates are made to a given data item, eventually all accesses to that item will return the last updated value. - This means that there is an acceptable window—the shorter the better—where a query request for specific entities may return a value that is inconsistent with the value set by the latest command request on those entities. Ideally, only a very small percentage of requests will fall into this inconsistency window. Do not use the Command-Query Separation pattern when: • You have a service that has strict transactional requirements (i.e. must conform to ACID constraints: atomic, consistent, isolated, durable) [2]
  • 5. Command-Query Separation Architecture Pattern 3 4/2/2020 5 Application Level 5.1 Structure 5.2 Participants Participant Responsibilities Entity.CommandHost (Price.CommandHost) • Exposes the EntityCommandService interface that provides create, update, and delete operations • Hosts the EntityCommandService implementation • Sends EntityEvents to Entity.Events topic • Maintains Entity state by creating, updating, and deleting Entities in the Entity.CommandDatastore Entity.UpdateHost (Price.UpdateHost) • Receives EntityEvents from Entity.Events topic • Hosts the EntityEventProcessor that processes incoming EntityEvents by creating, updating, and deleting EntityData in the EntityData.QueryDatastore Entity.QueryHost (Price.QueryHost) • Exposes the EntityQueryService interface that provides get operations • Hosts the EntityQueryService implementation that retrieves EntityData from the EntityData.QueryDatastore Entity.Events (Price.Events) • Publish-subscribe channel for EntityEvents Entity.CommandDatastore (Price.CommandDatastore) • Storage for Entities that are created and updated by Entity.CommandHost • Contains Entities in a persistent format Entity.QueryDatastore (Price.QueryDatastore) • High speed storage for EntityData that are created and updated by Entity.UpdateHost and retrieved by Entity.QueryHost • Contains EntityData in a format optimized for retrieval and return to clients
  • 6. Command-Query Separation Architecture Pattern 4 4/2/2020 5.3 Collaborations The Command-Query Separation pattern has the following collaborations: • Creating Entities - A command-client sends a CreateEntityRequest to the Entity.CommandHost through the EntityCommandService’s createEntity(request) API - The Entity.CommandHost processes the request and inserts the newly created Entity into the Entity.CommandDatastore - The Entity.CommandHost creates an EntityEvent (of event type CREATED and containing an EntityData transfer object mapped from the source Entity) and sends it to the Entity.Events topic - The Entity.UpdateHost receive the EntityEvent containing the created EntityData transfer object and inserts it into the Entity.QueryDatastore • Updating Entities - A command-client sends an UpdateEntityRequest to the Entity.CommandHost through the EntityCommandService’s updateEntity(request) API - The Entity.CommandHost processes the request and updates the modified Entity in the Entity.CommandDatastore - The Entity.CommandHost creates an EntityEvent (of event type Updated and containing an EntityData transfer
  • 7. Command-Query Separation Architecture Pattern 5 4/2/2020 object mapped from the source Entity) and sends it to the Entity.Events topic - The Entity.UpdateHost receive the EntityEvent containing the modified EntityData transfer object and updates it in the Entity.QueryDatastore • Getting Entities - A query-client sends a GetEntityRequest or GetEntitiesRequest to the Entity.QueryHost through the EntityQueryService’s getEntity(request) or getEntities(request) APIs. - The Entity.QueryHost processes the request by retrieving one or more EntityData objects from the Entity.QueryDatastore and returning them to the query-client • Deleting Entities - A command-client sends a DeleteEntityRequest to the Entity.CommandHost through the EntityCommandService’s deleteEntity(request) API - The Entity.CommandHost processes the request and deletes the specified Entity from the Entity.CommandDatastore - The Entity.CommandHost creates an EntityEvent (of event type DELETED and containing an EntityData transfer object mapped from the source Entity) and sends it to the Entity.Events topic - The Entity.UpdateHost receive the EntityEvent containing the deleted EntityData transfer object and deletes its instance from the Entity.QueryDatastore 6 Component Level Applications are composed by linking in components providing various elements of functionality: • Entity.CommandHost application links in the following components: - Entity.CommandService - Entity.CommandServer - Entity.Transfer • Entity.UpdateHost application links in the following components: - Entity.CommandService - Entity.CommandClient - Entity.QueryServer - Entity.Transfer • Entity.QueryHost application links in the following components: - Entity.QueryService - Entity.QueryServer - Entity.Transfer
  • 8. Command-Query Separation Architecture Pattern 6 4/2/2020 6.1 Structure 6.2 Participants Participant Responsibilities Entity.CommandService (Price.CommandService) • Contains modules providing service interfaces for Entity-related events and command type requests and replies (create, update, delete) Entity.CommandClient (Price.CommandClient) • Contains modules that implement the service interfaces and abstract classes declared in Entity.CommandService as client-side proxies [3] that can be used to the call the services implemented in Entity.CommandServer or to receive events emitted from the Entity.CommandHost application Entity.CommandServer (Price.CommandServer) • Contains modules that implement the interfaces declared in Entity.CommandService with the necessary business logic and other mechanisms such as internal application entities, repositories, and domain events to fully realize the Entity.CommandService microservice Entity.QueryService (Price.QueryService) • Contains modules providing service interfaces for Entity-related query type requests and replies (get) Entity.QueryClient (Price.QueryClient) • Contains modules that implement the service interfaces and abstract classes declared in Entity.QueryService as client-side proxies [3] that can be used to the call the services implemented in Entity.QueryServer Entity.QueryServer (Price.QueryServer) • Contains modules that implement the interfaces declared in Entity.QueryService with the necessary business logic and other mechanisms such as internal application entities, repositories, and domain events to fully realize the Entity.QueryService microservice Entity.Transfer (Price.Transfer) • Contains modules that implement the shared transfer objects [4] used to represent entities in requests, replies, and events.
  • 9. Command-Query Separation Architecture Pattern 7 4/2/2020 7 Module Level The Entity.CommandHost, Entity.UpdateHost, and Entity.QueryHost applications defined by the Command-Query Separation pattern are organized into layered architectures by applying the Layers architecture pattern [5] [6]. The specific layers of these applications are: • Client or Presentation Layer (implied) – consumer of the services and data provided by the Service Layer. • Service Layer – provides the contracts that represent the services offered by the system. • Application Layer – implements the contracts from the Service Layer by coordinating application activity of elements from the Service Layer and the Domain Layer. • Domain Layer – contains information about the domain. This is the heart of the business software. The state of business objects is held here. Persistence of the business objects and possibly their state is delegated to the Platform Layer. • Platform Layer – Provides a supporting library for all the other layers. - On server side – provides communication between layers, implements persistence for business objects, integration mechanisms such as HTTP invokers that receiving incoming requests and invoker the appropriate methods on service implementations. - On client-side – implements the contract from the Service Layer as proxies for client side use and provides supporting libraries for Presentation Layer A layer is represented by a component or one or more modules in a component. These components and modules are composed into larger networks of functionality. Modules are connected through interfaces to reduce coupling. Each module exports a set of provided interfaces and imports a set of required interfaces. The benefit of the modular architecture is that these components can be assembled into multiple configurations increasing software reuse and because the components are highly decoupled it is much easier to test them independently. 7.1 Entity.CommandHost Modules The following table lists the components of the Entity.CommandHost application and their constituent modules: Component Module Responsibilities Entity.CommandService RequestReply • Represents the “Service” layer of the Command Host application • Provides the EntityCommandService interface • Provides request/reply transfer objects [4] used in EntityCommandService - CreateEntityRequest/CreateEntityReply - UpdateEntityRequest/UpdateEntityReply - DeleteEntityRequest/DeleteEntityReply Event • Provides the EntityEventSender interface [7] Entity.Transfer Shared • Provides the EntityData transfer object used for transferring the Entity’s data Event • Provides the EntityEvent transfer object that represents entity events Entity.CommandServer Application • Represents the “Application” layer of the Command Host application • Provides the EntityCommandServiceImpl class • Provides the Mapper interface [8] Domain • Represents the “Domain” layer of the Command Host application • Provides the Entity domain class [9]
  • 10. Command-Query Separation Architecture Pattern 8 4/2/2020 • Provides the EntityRepository interface [10] [11] • Provides the EntityRepositoryImpl class Platform • Represents the “Platform” layer of the Command Host application • Provides the EntityCommandServiceEndpoint class • Provides the PlatformSpecificMapper class [8] • Provides the PlatformSpecificEntityEventSender class [7] 7.1.1 Structure 7.1.2 Participants «Module» Entity.CommandService.RequestReply Participant Responsibilities EntityCommandService (PriceCommandService) • Declares methods for creating, updating, and deleting Entities CreateEntityRequest (CreatePriceRequest) • Transfer Object that represents a request to create an Entity CreateEntityReply (CreatePriceReply) • Transfer Object that represents a reply to an associated request to create an Entity
  • 11. Command-Query Separation Architecture Pattern 9 4/2/2020 UpdateEntityRequest (UpdatePriceRequest) • Transfer Object that represents a request to update an Entity UpdateEntityReply (UpdatePriceReply) • Transfer Object that represents a reply to an associated request to update an Entity DeleteEntityRequest (DeletePriceRequest) • Transfer Object that represents a request to delete an Entity DeleteEntityReply (DeletePriceReply) • Transfer Object that represents a reply to an associated request to delete an Entity «Module» Entity.CommandService.Event Participant Responsibilities EntityEventSender (PriceEventSender) • Declares methods for sending EntityEvent transfer objects. «Module» Entity.Transfer.Shared Participant Responsibilities EntityData (PriceData) • Transfer Object that contains the transferrable data of an Entity «Module» Entity.Transfer.Event Participant Responsibilities EntityEvent (PriceEvent) • Transfer Object that represents the types of events that can occur on an Entity • Contains the EntityData that was the source of the event «Module» Entity.CommandServer.Application Participant Responsibilities EntityCommandServiceImpl (PriceCommandServiceImpl) • Server-side implementation of the PriceCommandService interface • Implements the method for creating, updating, and deleting Entities • Emits EntityEvents that correspond to each operation (create, update, delete) on Entities Mapper (PriceMapper) • Declares an interface for mapping Entities and related objects to and from their Transfer Object equivalents «Module» Entity.CommandServer.Domain Participant Responsibilities
  • 12. Command-Query Separation Architecture Pattern 10 4/2/2020 Entity (Price) • Represents a specific domain concept • Has a unique identity • Transitions through multiple states EntityRepository (PriceRepository) • Declares methods for inserting, updating, deleting, and retrieving Entities to/from a storage mechanism EntityRepositoryImpl (PriceRepositoryImpl) • Provides a storage specific implementation of the EntityRepository interface «Module» Entity.CommandServer.Platform Participant Responsibilities EntityCommandServiceEndpoint (PriceCommandServiceEndpoint) • Platform specific (e.g. HTTP/REST) endpoint for the service functionality declared in the EntityCommandService interface • Exposes EntityCommandService operations to clients PlatformSpecificMapper (ModelMapper) • Platform specific implementation of the Mapper interface PlatformSpecificEntityEventSender (KafkaAvroPriceEventSender) • Platform specific implementation of the EntityEventSender interface declared in the Entity.CommandService.Event module 7.1.3 Collaborations • EntityCommandServiceEndpoint receives platform specific requests, converts them to an application usable format such as Create | Update | Delete EntityRequest, and forwards them to an implementation of the EntityCommandService interface for processing • EntityCommandServiceImpl processes an incoming Create|Update|Delete EntityRequest by - inserting, updating, or deleting the Entity object specified in the request to/from the Entity.CommandDatastore through the EntityRepository - using a Mapper to map the Entity object to an EntityData transfer object - creating an appropriate EntityEvent transfer event object with a mapped EntityData as the event source - sending the EntityEvent to the Entity.Events topic using the EntityEventSender 7.2 Entity.UpdateHost Modules The following table lists the components of the Entity.UpdateHost application and their constituent modules: Component Module Responsibilities Entity.CommandService Event • Represents the “Service” layer of the Update Host application • Provides the EntityEventReceiver interface • Provides the EntityEventListener interface Entity.Transfer Shared • Provides the EntityData transfer object used for transferring the Entity’s data
  • 13. Command-Query Separation Architecture Pattern 11 4/2/2020 Event • Provides the EntityEvent transfer object that represents entity events Entity.QueryServer Application • Represents the “Application” layer of the Update Host application • Provides the EntityEventListenerImpl class • Provides the Mapper interface Domain • Represents the “Domain” layer of the Update Host application • Provides the EntityDataRepository interface Platform • Represents the “Platform” layer of the Update Host application • Provides the PlatformSpecificEntityDataRepositoryImpl class • Provides the PlatformSpecificEntityEventReceiver class 7.2.1 Structure
  • 14. Command-Query Separation Architecture Pattern 12 4/2/2020 7.2.2 Participants «Module» Entity.CommandService.Event Participant Responsibilities EntityEventReceiver (PriceEventReceiver) • Declares methods for starting/stopping receiving EntityEvent transfer objects • Declares methods for attaching/detaching an EntityEventListener to listen for EntityEvents EntityEventListener (PriceEventListener) • Declares an onEvent(EntityEvent) method to be called on receiving an incoming EntityEvent • Declares an onException(Exception) method to be called on encountering an exception while receiving an incoming EntityEvent «Module» Entity.Transfer.Shared Participant Responsibilities EntityData (PriceData) • Transfer Object that contains the transferrable data of an Entity «Module» Entity.Transfer.Event Participant Responsibilities EntityEvent (PriceEvent) • Transfer Object that represents the types of events that can occur on an Entity • Contains the EntityData that was the source of the event «Module» Entity.QueryServer.Application Participant Responsibilities EntityEventListenerImpl (PriceEventListenerImpl) • Implements EntityEventListener interface • Creates, updates, and deletes EntityData objects using EntityDataRepository «Module» Entity. QueryServer.Domain Participant Responsibilities EntityDataRepository (PriceDataRepository) • Declares methods for inserting, updating, deleting, and retrieving EntityData objects to/from a storage mechanism «Module» Entity. QueryServer.Platform
  • 15. Command-Query Separation Architecture Pattern 13 4/2/2020 Participant Responsibilities PlatformSpecificEntityDataRepositoryImpl (RedisPriceDataRepository) • High speed storage specific implementation of the EntityDataRepository interface PlatformSpecificEntityEventReceiver (KafkaAvroPriceEventReceiver) • Platform specific implementation of the EntityEventReceiver interface declared in the Entity.CommandService.Event module 7.2.3 Collaborations TBD 7.3 Entity.QueryHost Modules The following table lists the components of the Entity.QueryHost application and their constituent modules: Component Module Responsibilities Entity.QueryService RequestReply • Represents the “Service” layer of the Query Host application • Provides the EntityQueryService interface • Provides request/reply transfer objects [4] used in EntityQueryService - GetEntityRequest/GetEntityReply - GetEntitiesRequest/GetEntitiesReply Entity.Transfer Shared • Provides the EntityData transfer object used for transferring the Entity’s data Entity.QueryServer Application • Represents the “Application” layer of the Query Host application • Provides the EntityQueryServiceImpl class Domain • Represents the “Domain” layer of the Query Host application • Provides the EntityDataRepository interface Platform • Represents the “Platform” layer of the Query Host application • Provides the PlatformSpecificEntityDataRepositoryImpl class • Provides the EntityQueryServiceEndpoint class
  • 16. Command-Query Separation Architecture Pattern 14 4/2/2020 7.3.1 Structure 7.3.2 Participants «Module» Entity.QueryService.RequestReply Participant Responsibilities EntityQueryService (PriceQueryService) • Declares methods for retrieving EntityData GetEntityRequest (GetPriceRequest) • Transfer Object that represents a request to retrieve a single EntityData GetEntityReply (GetPriceReply) • Transfer Object that represents a reply to an associated request to retrieve a single EntityData GetEntitiesRequest (GetPricesRequest) • Transfer Object that represents a request to retrieve multiple EntityData
  • 17. Command-Query Separation Architecture Pattern 15 4/2/2020 GetEntitiesReply (GetPricesReply) • Transfer Object that represents a reply to an associated request to retrieve multiple EntityData «Module» Entity.Transfer.Shared Participant Responsibilities EntityData (PriceData) • Transfer Object that contains the transferrable data of an Entity «Module» Entity.QueryServer.Application Participant Responsibilities EntityQueryServiceImpl (PriceQueryServiceImpl) • Server-side implementation of the EntityQueryService interface • Implements the methods for retrieving EntityData «Module» Entity. QueryServer.Domain Participant Responsibilities EntityDataRepository (PriceDataRepository) • Declares methods for inserting, updating, deleting, and retrieving EntityData objects to/from a storage mechanism «Module» Entity. QueryServer.Platform Participant Responsibilities PlatformSpecificEntityDataRepositoryImpl (RedisPriceDataRepository) • High speed storage specific implementation of the EntityDataRepository interface EntityQueryServiceEndpoint (PriceQueryServiceEndpoint) • Platform specific (e.g. HTTP/REST) endpoint for the service functionality declared in the EntityQueryService interface • Exposes EntityQueryService operations to clients 7.3.3 Collaborations TBD 8 Consequences Benefits • High Performance Queries –
  • 18. Command-Query Separation Architecture Pattern 16 4/2/2020 • Robust Horizontal Scalability – • Geographical Distribution – Liabilities • Inconsistency Window – • Introduces Complexity – Trade-offs • Eventual Consistency vs High Performance Queries & Horizontal Scalability – 9 Implementation The following implementation issues should be considered when implementing the Command-Query Separation pattern: • Multi-Component Workspace Organization – The development workspace for a system following the Command- Query Separation pattern will be composed of multiple applications and multiple components. A suggested way of organizing this workspace is to group the code and resources for applications, components, and tests into separate directories that clearly indicate the purpose of each element: - Applications  The executable artifacts that host the system’s functionality  Applications are constructed by importing the required Components and wiring them together using a dependency injection mechanism  A system has one or more applications - Components  The composable artifacts that provide interfaces and implementations that make up the system’s microservices and other mechanisms  A typical microservice is implemented with three Components: ▪ A service component that defines the contract of the microservice ▪ A server component that implements the business logic of the microservice ▪ A client component that provides proxies for consuming the microservice - Tests  The executable artifacts that verify the operational semantics of the elements defined in the system’s Components  Tests should be separate from Components  Tests can be mapped to Components in many ways (the example below follows a one-to-one mapping) The directory structure of such a system would look like the following:  Applications/  Price.CommandHost/
  • 19. Command-Query Separation Architecture Pattern 17 4/2/2020  Price.UpdateHost/  Price.QueryHost/  Components/  Price.CommandService/  Price.CommandClient/  Price.CommandServer/  Price.QueryService/  Price.QueryClient/  Price.QueryServer/  Price.Transfer/  Tests/  Price.CommandServiceTest/  Price.CommandClientTest/  Price.CommandServerTest/  • • • This structure can be realized in many popular development environments. If you are implementing on a JVM platform you can use an IDE such as IntelliJ IDEA. In IDEA, workspaces are called projects and elements such as applications, components, and tests are called modules. IDEA enables the creation of multi-module projects that correspond to the above suggested directory structure (see figure below).
  • 20. Command-Query Separation Architecture Pattern 18 4/2/2020 If you are implementing on the .Net platform you can use the Visual Studio IDE. In Visual Studio, workspaces are called solutions and applications, components, and tests are called projects. Visual Studio enables the creation of multi-project solutions that correspond to the suggested directory structure (see figure below).
  • 21. Command-Query Separation Architecture Pattern 19 4/2/2020 Dependency management and build tools such as Maven also can be configured to support multi-component workspaces. Maven follows the same naming convention as IntelliJ IDEA (or rather IDEA follows Maven’s naming convention). Workspaces are called projects and applications, components, and tests are called modules. Below is a multi-module Maven POM file that supports our suggested directory structure: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>nrhl</groupId> <artifactId>Cms.Price</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>Components/Cms.Price.Transfer</module> <module>Components/Cms.Price.CommandService</module> <module>Components/Cms.Price.CommandServer</module> <module>Components/Cms.Price.CommandClient</module> <module>Components/Cms.Price.QueryService</module> <module>Components/Cms.Price.QueryServer</module> <module>Components/Cms.Price.QueryClient</module> <module>Tests/Cms.Price.TransferTest</module> <module>Tests/Cms.Price.CommandServiceTest</module> <module>Tests/Cms.Price.CommandServerTest</module> <module>Tests/Cms.Price.CommandClientTest</module>
  • 22. Command-Query Separation Architecture Pattern 20 4/2/2020 <module>Tests/Cms.Price.QueryServiceTest</module> <module>Tests/Cms.Price.QueryServerTest</module> <module>Tests/Cms.Price.QueryClientTest</module> <module>Applications/Cms.Price.CommandHost</module> <module>Applications/Cms.Price.UpdateHost</module> <module>Applications/Cms.Price.QueryHost</module> </modules> </project> Maven is often used inside of IntelliJ IDEA to configure dependency management and builds. • Applications, Components, Tests, and Artifacts – Applications, components, and tests are logical elements that contain source code and other resources. They are logical in the sense that they specify the parts of the system. During the system build process, the source code and resources contained in the application, component, and test elements gets compiled and packaged into artifacts. Artifacts are files that contain executable or interpretable code and are deployable elements in the sense that they can be deployed to an execution environment and run in some fashion. Application are executable artifacts in that they can be executed directly as in the case with .exe files or indirectly as in the case *.jar files that contain a main entry point (a class with a static main method). Components are composable artifacts in that they contain executable code, but they must be linked into an Application to be executed. This is because components lack a main entry point to start application execution. On the .Net platform, components are compiled and packaged into *.dll files while on the JVM platform components are compiled into *.jar files. Tests are essentially components but contain test code instead of application code. • Modules and Dependency Injection – Each component in the system is further organized into one or more modules that can be composed into larger networks of functionality. These modules are connected through interfaces to reduce coupling. Each module exports a set of provided interfaces and imports a set of required interfaces. The benefit of the modular architecture is that these components can be assembled into multiple configurations increasing software reuse and because the components are highly decoupled it is much easier to test them independently [12]. Modules are also an important concept in dependency injection where they are a mechanism to define a set of related dependency bindings. The dependency injection modules used by an application are loaded into the dependency injection frameworks injector or container to wire together objects based on the bindings defined in the modules. These two ideas—modules as logical groupings of functionality and modules as related dependency injection bindings—align very well and is one of the main reasons that module-based dependency injection is a powerful tool for organizing software. As mentioned above, logically, modules export a set of provided interfaces and import a set of required interfaces. The following Java example illustrates how a module with provided and required interfaces are specified using the Guice dependency injection framework [13]. The PriceApplicationModule is a class that extends the Guice AbstractModule base class. All Guice modules are derived from AbstractModule and is the means by which the Guice Injector identifies modules. public class PriceApplicationModule extends AbstractModule { @Override protected void configure() { bind(IPriceCommandService.class) .to(PriceCommandService.class) .in(getDefaultScope()); bind(ModelMapper.class)
  • 23. Command-Query Separation Architecture Pattern 21 4/2/2020 .toProvider( () -> new ModelMapper() .registerModule(new PriceMappingModule())) .in(Scopes.SINGLETON); . . . } } PriceApplicationModule overrides the configure() method from AbstractModule to define the dependency injection bindings of the module. In the above code example, the IPriceCommandService interface is being bound to the PriceCommandService concrete class. IPriceCommandService is one of the provided interfaces of the module. The required interfaces of the PriceApplicationModule are specified by the parameters of the PriceCommandService constructor. public class PriceCommandService extends AbstractService implements IPriceCommandService, IPriceDomainEventObserver { private final IPriceRepository itsRepository; private final IPriceEventSender itsSender; private final ModelMapper itsMapper; @Inject public PriceCommandService( IUnitOfWorkProvider p, IActionQueue q, IPriceRepository r, IPriceEventSender s, ModelMapper m) { super(p,q); itsRepository = r; itsSender = s; itsMapper = m; } . . . } The PriceCommandService constructor has the following parameters types: - IUnitOfWorkProvider - IActionQueue - IPriceRepository - IPriceEventSender - ModelMapper These types—excluding ModelMapper—are some of the required interfaces of the PriceApplicationModule and are the provided interfaces of other modules that the PriceApplicationModule depends on. ModelMapper is not a required interface because its binding is defined in PriceApplicationModule and is technically a provided interface that is injected into PriceCommandService. The following diagram illustrates these dependencies.
  • 24. Command-Query Separation Architecture Pattern 22 4/2/2020 PriceDomainModule and PricePlatformModule also extend AbstractModule and override configure() with dependency injection bindings for their respective provided interfaces. Modules are also a means of introducing a layered architecture to the system [6] [5]. The PriceApplicationModule, PriceDomainModule, and PricePlatformModule correspond respectively to the Application, Domain, and Platform layers of the Price.CommandHost application (see section 7 Module Level). • High Performance Event Processing – One of the trade-offs of using the Command-Query Separation pattern is that the solution is inherently eventually consistent so it can support high performance and horizontal scalability. Eventual consistency means that there will be a window of time between the point that the Command Host commits a create, update, or delete operation to the command datastore and the point that the Update Host receives the event associated with that operation and updates the query datastore with the new state. We want to make that window as small as possible to avoid inconsistency between the command state and the query state. A key element in reducing the inconsistency window is to optimize the event processing between the command host and the update host. There are multiple things that can be done to optimize event processing: - Select a high performance messaging or streaming platform such as Apache Kafka [14]. Kafka is a distributed data store optimized for ingesting and processing streaming data in real-time. The primary elements in Kafka are:
  • 25. Command-Query Separation Architecture Pattern 23 4/2/2020  Topics and Partitions – A topic is a named stream to which records are published. Topics can have zero, one, or many consumers that subscribe to the data written to it. Each topic stores its data in a partitioned log managed by the Kafka cluster: Each partition is an ordered, immutable sequence of records that is continually appended to—a structured commit log. The records in the partitions are each assigned a sequential id number called the offset that uniquely identifies each record within the partition. Partitions are the unit of concurrency in Kafka.  Producers – A producer publishes data to a topic by appending data records to the partitions contained in the topic.  Consumers and Consumer Groups – A consumer subscribes to a topic in order to consume data that has been published to the topic by producers. Each consumer is a member of a consumer group which is a mechanism that enables the topic to deliver a published record to one and only one consumer in a specific consumer group. If multiple consumer instances are part of the same consumer group, then records published on the topic will effectively be load balanced over the consumer instances. If multiple consumer instances have different consumer groups, then each record published on the topic will be broadcast to each of the consumer instances. - Assign a consumer group for each region. Each geographical region will contain one or more consumer groups to enable a broadcast of EntityEvents to Entity.UpdateHosts in all regions. - Partition Kafka topics to maximize throughput. The Entity.Events topic should be created with at least the same number of partitions as the maximum number of consumers instance in the consumers groups. Let N be number of partitions for the Entity.Events topics and {C1, C2, …, Ci} be the set of consumers in each of the system’s consumer groups, then 𝐍 ≥ 𝑚𝑎𝑥(𝐶1, 𝐶2, … , 𝐶𝑖) - Ensure that entity events are delivered in order. When Kafka topics have more than one partition, producers must determine which partition to write the event. Kafka provides a mechanism for controlling which partition is written to by using a key during event publishing. The producer selects the partition by calculating the modulo of the key’s hash code to the number of partitions in the topic: 𝑝𝑎𝑟𝑡𝑖𝑡𝑖𝑜𝑛𝐼𝑛𝑑𝑒𝑥 = 𝑘𝑒𝑦. 𝑔𝑒𝑡𝐻𝑎𝑠ℎ𝐶𝑜𝑑𝑒() 𝒎𝒐𝒅 𝑛𝑢𝑚𝑏𝑒𝑟𝑂𝑓𝑃𝑎𝑟𝑡𝑖𝑡𝑖𝑜𝑛𝑠 EntityEvent transfer objects contain an EntityData that is the source of the event: If we use the EntityData’s entityId property as the key when publishing the EntityEvent to the topic, the producer
  • 26. Command-Query Separation Architecture Pattern 24 4/2/2020 will write the events generated by a specific Entity to the same partition in the topic, thus ensuring that these events will be delivered in order. The key can be set during ProducerRecord construction: String topic = "Price.Events"; PriceEvent event = new PriceEvent().setSource(new PriceData(...)); String key = event.getSource().getPriceId(); ProducerRecord<String,PriceEvent> record = new ProducerRecord<>(topic,key,event); - Use Apache Avro [15] to generate transfer objects and for serialization/deserialization. Kafka offers both string- based and binary-based options for message serialization/deserialization. String-based serialization/deserialization is convenient because it generally produces human readable data that makes it easier for engineers to read and debug. But string-based serialization/deserialization is slower than binary-based mechanisms and the size of string- based messages is much larger that the size of binary-based messages of the same data. Apache Avro is a binary- based serialization/deserialization implementation that is well integrated with Kafka. In Avro, transfer objects are defined using Avro Schema files. The following are the Avro Schema definitions of PriceEvent and related types: { "type" : "record", "namespace" : "cms.price.transfer.event", "name" : "PriceEvent", "fields" : [ {"name" : "identifiers", "type": "strata.foundation.core.event.EventIdentifiersData"}, {"name" : "eventType", "type": "strata.foundation.core.event.StandardEventType"}, {"name" : "source", "type": "cms.price.transfer.shared.PriceData"}, {"name" : "exception", "type": ["null","strata.foundation.core.event.ExceptionData"], "default" : null} ] } { "type" : "record", "namespace" : "cms.price.transfer.shared", "name" : "PriceData", "fields" : [ {"name": "priceId", "type": ["null","long"], "default": null}, {"name": "productId", "type": ["null","long"], "default": null}, {"name": "currencyCode", "type": "string"}, {"name": "msrp", "type": "double"}, {"name": "regularPrice", "type": "double"}, {"name": "discounts", "type": {"type": "array", "items": "cms.price.transfer.shared.DiscountData"}} ] } { "type" : "record", "namespace" : "cms.price.transfer.shared", "name" : "DiscountData", "fields" : [ {"name": "discountName", "type": "string"}, {"name": "price", "type": "double"}, {"name": "percentage", "type": "double"} ] } These schema definitions are used to generate language-specific classes that are used by the application and clients to produce and consume PriceEvents. • Object to Object Mapping – Implementations following the Command-Query Separation pattern often use the Transfer Object pattern [4] when building the request and reply objects that are inputs and outputs of the EntityCommandService and the EntityQueryService interfaces. Transfer Objects in turn rely on the Mapper pattern [8] to map Transfer Objects to and from Entities [4]. These mappers do not need to be built from scratch. There are several excellent object mapper frameworks that do much of the work already. The Model Mapper framework [16] is available on the JVM platform and the AutoMapper framework [17] is available on the .Net platform. Both frameworks use a convention-based mapping strategy that automatically maps properties in source and destination object that have the same names and types. For more complex mappings that are not supported by the default conventions, the frameworks provide fluent interfaces for configuring the mappers as needed. The following is an example of configuring a Model Mapper mapping
  • 27. Command-Query Separation Architecture Pattern 25 4/2/2020 module for the Price.CommandServer component in Java: public class PriceMappingModule implements Module { @Override public void setupModule(ModelMapper mapper) { mapper .createTypeMap(PriceData.class,IPrice.class) .setProvider(request -> new StandardPrice()) .addMapping(PriceData::getPriceId,IPrice::setPrimaryId) .addMappings( mapping -> mapping .using(new DiscountDataListToDiscountMapConverter()) .map(PriceData::getDiscounts,IPrice::setDiscounts)); mapper .createTypeMap(IPrice.class,PriceData.class) .include(StandardPrice.class,PriceData.class) .addMapping(IPrice::getPrimaryId,PriceData::setPriceId) .addMappings( mapping -> mapping .using(new DiscountMapToDiscountDataListConverter()) .map(IPrice::getDiscounts,PriceData::setDiscounts)); . . . } } In the above code example, the PriceMappingModule is a class that defines all of the object-to-object mapping used in the Price.CommandServer component. PriceMappingModule extends Model Mapper framework’s Module class which is the base class of all mapping modules in Model Mapper. In the first mapping configuration, a mapping relationship is created between the PriceData class (a transfer object) and the IPrice interface (an entity). Because IPrice is an interface the configuration specifies the concreate StandardPrice class that implements IPrice with the setProvider() method. Most of the properties in PriceData and IPrice have the same names and types so the convention-based mappings are automatically used. The exception to this is the PriceId property in PriceData and the PrimaryId property in IPrice. The PriceId property identifies the price object and is mapped to the PrimaryId property. Another mismatch between the PriceData transfer object and the IPrice entity is that the Discounts property is represented by different collection types. In PriceData, the Discounts property is a list of DiscountData objects. In IPrice, the Discounts property is a map of Discount objects. The DiscountDataListToDiscountMapConverter class is a converter that facilitates the conversion between the list and map types. In the second mapping configuration, the opposite mapping relationship is created where IPrice is the source and PriceData is the destination. • Repositories – The domain layers in both the Entity.CommandServer component and the Entity.QueryServer components can be implemented using the Repository pattern [10] [11]. In the CommandServer component, the EntityRepository is implemented with a relational database and an object-to-relational mapping framework such as Hibernate. In the QueryServer component, the EntityDataRepository is implemented with a high speed key-value store or a document store and some type of serialization/deserialization between the EntityData type and a string-based format such as JSON. • Key-Value Store vs Document Store – The following criteria is useful when deciding on what kind of high speed datastore is needed for the query side of the system: - If the domain of the service only requires simple key-based queries such as retrieving an object with a key or a set of objects with multiple keys, then consider using a key-value datastore such as Redis.
  • 28. Command-Query Separation Architecture Pattern 26 4/2/2020 - If the domain of the service requires more complex queries that select objects based on properties other than a simple key, then consider using a document store such as Couchbase or Mongo. • Service Endpoints – Services are exposed to clients through service endpoint classes. These classes are usually part of a REST or Web Service framework and provide annotations (JVM platform) or attributes (.Net platform) that specify the paths or routes, http request type (POST, PUT, GET, DELETE), and request and response content. The service endpoint class can also use annotations from an api specification framework such as Open API [18] or Swagger [19] to generate API documentation and executable API proxies for a variety of client platforms. The following is a code example showing the PriceQueryServiceEndpoint class that uses the JAX-RS framework [20] to define the service endpoint and Open API to generate API documentation and proxies: @Path("price-query") @Tag( name = "PriceQueryService", description = "Service that provides create, update, delete, activate, " + "and deactivate operations for prices.") public class PriceQueryServiceEndpoint { private IPriceQueryService itsImplementation; @Inject public void setImplementation(IPriceQueryService impl) { itsImplementation = impl;} @Path("get-price/{requestedPriceId}") @GET @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Gets an existing price") @ApiResponse( responseCode = "200", description = "successful reply", content = @Content( mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = GetPriceReply.class))) @ApiResponse( responseCode = "500", description = "exception", content = @Content( mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = GetPriceReply.class))) public CompletionStage<GetPriceReply> getPrice( @PathParam("requestedPriceId") Long requestedPriceId, @QueryParam("correlationId") UUID correlationId) { return itsImplementation.getPrice( (GetPriceRequest) new GetPriceRequest() .setRequestedPriceId(requestedPriceId) .setCorrelationId(correlationId) .setTimestamp(Instant.now())); } . . . } PriceQueryServiceEndpoint also uses dependency injection to inject an instance of the IPriceQueryService interface that provides the actual implementation of the service endpoint. This approach keeps the business logic defined in the class implementing IPriceQueryService separate from the platform specific details of PriceQueryServiceEndpoint. This separation of concerns enables multiple service endpoints if needed or an easy path for moving to a different endpoint framework if the need arises.
  • 29. Command-Query Separation Architecture Pattern 27 4/2/2020 • Scaling Horizontally with Containers, Clusters, and Container Orchestration – In order to scale horizontally, each of the applications in the system—Entity.CommandHost, Entity.UpdateHost, and Entity.QueryHost—are built to be executed in a container environment such as Docker and the instances of these containers are deployed to one or more clusters such as Amazon EC2 or Google Cloud Platform. The number of container instances are automatically scaled by the cluster’s container orchestration mechanism such as Kubernetes (Amazon EKS or Google GKE) or Amazon ECS. The following is an example Dockerfile that defines the Docker image of Price.UpdateHost. FROM openjdk:8-jre-alpine ENV DEPLOY_ENV docker-development EXPOSE 8097 COPY target/Cms.Price.UpdateHost-1.0-SNAPSHOT-jar-with-dependencies.jar /cms-price-updatehost.jar CMD ["/usr/bin/java", "-jar", "-Xmx1024m", "/cms-price-updatehost.jar"] The following diagram illustrates a common deployment pattern for systems following the Command-Query Separation pattern:
  • 31. Command-Query Separation Architecture Pattern 29 4/2/2020 10 Sample Code Sample code for the Price Service used in the motivation section’s example can be found at: https://hautelook.sharepoint.com/:u:/s/catalogmanagement/ESrzh_sjOn5Ns1RXuF9ZtrQBBFFwkUWWXcTzQzYy4QpEfQ?e=q53Ak1 11 Known Uses TBD 12 Related Patterns • Layers – The Layers Pattern [6] [5] • Transfer Object – The Transfer Object Pattern [4] • Mapper – The Mapper Pattern [8] • Proxy – The Proxy Pattern [3] • Messaging Gateway – The Messaging Gateway Pattern [7] • Publish-Subscribe Channel – The Publish-Subscribe Channel Pattern [21] • Repository – The Repository Pattern [10] [11] • Unit-Of-Work – The Unit-Of-Work Pattern [22] • Service Assembly – The Service Assembly Pattern [TBD] 13 References [1] Peter Bailis and Ali Ghodsi, "Eventual Consistency Today: Limitations, Extensions, and Beyond," Queue, vol. 11, no. 3, March 2013. [Online]. https://dl.acm.org/doi/pdf/10.1145/2460276.2462076 [2] Wikipedia Community. wikipedia.org. [Online]. https://en.wikipedia.org/wiki/ACID [3] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, "Proxy," in Design Patterns: Elements of Reusable Object-Oriented Software.: Addison-Wesley, 1995, pp. 207-212. [4] Martin Fowler, "Data Transfer Object," in Patterns of Enterprise Application Architecture.: Addison-Wesley Professional, 2003, pp. 401-413. [5] Eric Evans, "Layers," in Domain-Driven Design: Tackling Complexity in the Heart of Software.: Addison-Wesley, 2004, pp. 71-72. [6] Frank Buschman, Regine Meunier, Hans Rohnert, Peter Sommerlad, and Michael Stal, "Layers," in Pattern- Oriented Software Architecture: A System of Patterns.: John Wiley & Sons, 1996, pp. 31-33. [7] Gregor Hohpe and Bobby Woolfe, "Messaging Gateway," in Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions.: Addison-Wesley, 2004.
  • 32. Command-Query Separation Architecture Pattern 30 4/2/2020 [8] Martin Fowler, "Mapper," in Patterns of Enterprise Application Architecture.: Addison-Wesley Professional, 2003, pp. 473-474. [9] Eric Evans, "Entities," in Domain-Driven Design: Tackling Complexity in the Heart of Software.: Addison-Wesley, 2004, pp. 90-99. [10] Martin Fowler, "Repository," in Patterns of Enterprise Application Architecture.: Addison-Wesley Professional, 2003, pp. 322-327. [11] Eric Evans, "Repository," in Domain-Driven Design: Tackling Complexity in the Heart of Software.: Addison- Wesley, 2004, pp. 158-159. [12] Eric Evans, "Modules," in Domain-Driven Design: Tackling Complexity in the Heart of Software.: Addison-Wesley, 2004, p. 111. [13] Google. (2015) github.com/google/guice. [Online]. https://github.com/google/guice/wiki/Motivation [14] Apache Software Foundation. (2017) kafka.apache.org. [Online]. https://kafka.apache.org/intro [15] Apache Software Foundation. (2012) avro.apache.org. [Online]. https://avro.apache.org/docs/current/ [16] Jonathan Halterman. (2019) modelmapper.org. [Online]. http://modelmapper.org/getting-started [17] Jimmy Bogard. (2017) automapper.readthedocs.io. [Online]. https://automapper.readthedocs.io/en/latest/Getting- started.html [18] Open API Initiative. (2018) spec.openapis.org. [Online]. http://spec.openapis.org/oas/v3.0.2 [19] Smart Bear, Inc. (2019) swagger.io. [Online]. https://swagger.io/docs [20] JAX-RS Specification Committee. (2017) github.com/jax-rs. [Online]. https://github.com/jax- rs/spec/blob/master/spec.pdf [21] Gregor Hohpe and Bobby Woolfe, "Publish-Subscribe Channel," in Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions.: Addison-Wesley, 2004, pp. 105-106. [22] Martin Fowler, "Unit of Work," in Patterns of Enterprise Application Architecture.: Addison-Wesley Professional, 2003, pp. 184-194.