ROME 11-12 april 2014 ROME 11-12 april 2014 Moving Away From CRUD: Domain Driven Design (DDD) and Command Query Responsibility Segregation (CQRS) Email: [email protected] Twitter: @foratlatif Forat Latif
Sep 07, 2014
ROME 11-12 april 2014ROME 11-12 april 2014
Moving Away From CRUD: Domain Driven Design (DDD) and Command Query Responsibility Segregation (CQRS)
Email: [email protected]: @foratlatif
Forat Latif
ROME 11-12 april 2014 - Speaker’s name
Time to start a greenfield project
ROME 11-12 april 2014 - Speaker’s name
Process
1) Gather Requirements
2) Model our “Entities”
3) Scaffold our projects and our database based on those entities
ROME 11-12 april 2014 - Speaker’s name
What the Architecture looks like?
ROME 11-12 april 2014 - Speaker’s name
Our Domainclass Product {
String name;String
description;Integer price;Integer
discount;String
category;}
class Customer {Address
address;String name;
}
class Order {String
description;Customer
customer;
List<LineItems> lineItems;}
class LineItem {Product
product;}
ROME 11-12 april 2014 - Speaker’s name
Just what do we mean by Entity?class Product {
String name;String description;Integer price;Integer discount;String category;
String getDescription() {return this.description;
}void setDescription(String aDescription) {
this.description = aDescription; }
String getName() { …….
……}
ROME 11-12 april 2014 - Speaker’s name
So … Where is the business logic?class ProductService {
public void discountProduct(String productId, Integer discount) {Product product = productRepository.find(productId);product.setDiscount(discount);if (discount > 30) {
product.setStatus(“Super Discount”);}productRepository.save(aProduct);
}}
ROME 11-12 april 2014 - Speaker’s name
So … Where is the business logic?class ProductController {
public void updateProduct(Product product) {productRepository.save(product);
}}
ROME 11-12 april 2014 - Speaker’s name
Are we really doing OOP?
Objects are supposed to have a state and behavior that modifies that state
State shouldn’t be exposed (getters and setters are not encapsulation)
Objects should be loosely coupled
Business rules should be placed in one place
class Product {String name;String description;String category;
String getName() {return
this.description;}void setName(String name) {
this.name= name; }
String getPrice() { …….
……}
ROME 11-12 april 2014 - Speaker’s name
Implicit Business Rulesclass PromoService {
void discountProduct (String productId, Integer discount) {Product product =
productRepository.find(productId);product.setDiscount(discount);if (discount > 30) {
product.setStatus(“Super Discount”);}
}}class AnotherService {
void discountSomethinElse (String productId) {Product product =
productRepository.find(productId);someValue = calculateValue();product.setDiscount(someValue);
}}
ROME 11-12 april 2014 - Speaker’s name
Objects should expose BEHAVIOR
class Product {String name;String status;Integer price;Integer discount;String category;
void discount(Integer discount) {this.discount = discount;if (discount > 30) {
this.status = “Super Discount”;}
}}
ROME 11-12 april 2014 - Speaker’s name
Back to our “Entities”
class Customer {String name;Address address;
}
class Address {String street;Integer streetNumber;
}
ROME 11-12 april 2014 - Speaker’s name
Entities and Value Objects
Entities in DDD are not defined by their attributes, they are defined by a conceptual identity and we care about its changes
Value Objects represent a description of something at a certain point in time. Thats why they are immutable and we care only about its attributes
ROME 11-12 april 2014 - Speaker’s name
What a purchase looks like
class OrderService {@Transactionalpublic void purchaseOrder(String orderId) {
Order order = orderRepository .find(orderId);order.purchase();
productService.removeFromInventory(order.products());
emailService.sendConfirmationEmail(order);}
}
ROME 11-12 april 2014 - Speaker’s name
Suddenly our mail server goes down …
ROME 11-12 april 2014 - Forat Latif
Domain Events
A Domain Event, represents something that already happened, that is relevant to the business
ROME 11-12 april 2014 - Forat Latif
Domain Events
class Service {
void handle(SomeEvent event) {doSomething();
}
}
ROME 11-12 april 2014 - Forat Latif
Domain Eventsclass OrderService {
@Transactionalpublic void purchaseOrder(String orderId) {
Order order = orderRepository .find(orderId);order.purchase();
productService.removeFromInventory(order.products());eventBus.push(new
OrderPurchased(order.orderId());}
}class EmailSender {
public void handle(OrderPurchased orderPurchased) {Order order =
orderRepository.find(orderPurchased.orderId);emailService.sendConfirmationEmail(order);
}
}
ROME 11-12 april 2014 - Forat Latif
Sales of a Product skyrocket ….
ROME 11-12 april 2014 - Forat Latif
Eventual Consistencyclass OrderService {
@Transactionalpublic void purchaseOrder(String orderId) {
Order order = orderRepository .find(orderId);order.purchase();eventBus.push(new
OrderPurchased(order.orderId());}
}class OrderProcessManager {
public void handle(OrderPurchased orderPurchased) {Order order =
orderRepository.find(orderPurchased.orderId);
productService.removeFromInventory(order.products());}
}
ROME 11-12 april 2014 - Forat Latif
Aggregate Roots
Aggregate is a pattern in Domain-Driven Design. A DDD aggregate is a cluster of domain objects that can be treated as a single unit.
An aggregate has a root that acts as an outside interface and keeps all invariants inside the aggregate.
Each request should strive to load one aggregate and execute a single operation on it.
Hence, Aggregates guarantee transactional consistency inside its boundaries
ROME 11-12 april 2014 - Forat Latif
What our new Architecture looks like
ROME 11-12 april 2014 - Forat Latif
Product Inventory and Catalog
class Product {String name;Integer price;
Integer priceFromSupplier;String supplier;
String shippingStatus;String shippingMethod;
}
ROME 11-12 april 2014 - Forat Latif
Bounded Contexts
Explicitly define the context within which a model applies. Explicitly set boundaries in terms of team organization, usage within specific parts of the application, and physical manifestations such as code bases and database schemas. Keep the model strictly consistent within these bounds, but don’t be distracted or confused by issues outside.
Inventory
E-commerce
Shipping
ROME 11-12 april 2014 - Forat Latif
Bounded Contexts
class Product {String name;Integer price;
} class Item {
String shippingStatus;String shippingMethod;
}class Product {
Integer priceFromSupplier;
String supplier;}
E-Commerce Context
Inventory Context
Shipping Context
ROME 11-12 april 2014 - Forat Latif
Communication between BCs
Inventory
E-commerce
Shipping
ROME 11-12 april 2014 - Forat Latif
How to identify BCs
Ubiquitous Language
Goals and Concerns
Teams
Organizational Structure
ROME 11-12 april 2014 - Forat Latif
So .. just what is DDD?
Domain Driven Design is an approach to software development in which the Domain is the center of attention.
- Strategic Design
- Tactical Design
We should focus on what matters, and thats adding value to our business through software, the software itself is just a byproduct of our strategy
ROME 11-12 april 2014 - Forat Latif
What about User Interfaces?
class ProductController {
Product viewProduct (String productId) {Product product =
productRepository.find(productId);return product;
}
}
ROME 11-12 april 2014 - Forat Latif
User dashboard
ROME 11-12 april 2014 - Forat Latif
User Dashboard
class ProductController {
ProductDTO viewProduct (String productId) {
//Solution 1: Do a manual query and map it to the DTO//Solution 2: Use your ORM to get all the objects you
need // and map them to the DTO
return productDTO;}
}
ROME 11-12 april 2014 - Forat Latif
List the products of a category
ROME 11-12 april 2014 - Forat Latif
Category Tree
ROME 11-12 april 2014 - Forat Latif
Category Tree Model
class Category {String name;Category parent;
}
class Product{Category category;
}
ROME 11-12 april 2014 - Forat Latif
“Simple” change request …
ROME 11-12 april 2014 - Forat Latif
Possible Solutions
Nested Query:
with CTQ as ( select * from Categories c where c.parentId = 1 union all select * from CTQ p, Categories c where c.parentId = p.categoryId)select * from CTQ
ROME 11-12 april 2014 - Forat Latif
Possible Solutions
Modify The domain:
//Impedance Mismatch Pain
class Category {List<Product> products;List<Category> children;
}
class Product {Category category;
}
ROME 11-12 april 2014 - Forat Latif
Possible Solutions
Another possibility:
class Category {String parent1Id;String parent2Id;String parent3Id;
….
String categoryId;String name;
}
ROME 11-12 april 2014 - Forat Latif
Command Query Separation (CQS) Principle
Command–query separation (CQS) is a principle that states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both. In other words, asking a question should not change the answer
Commands and Queries have different concerns
ROME 11-12 april 2014 - Forat Latif
Command Query Responsibility Segregation (CQRS)
We can do CQS at the architectural level too .....
class CategoryReadService{List<Categories> getChildrenCategories(String
categoriId);}
class CategoryWriteService{void addCategory (String parendId, String name);
}
ROME 11-12 april 2014 - Forat Latif
Command Query Responsibility Segregation (CQRS)
where are do we need to do queries? when returning a DTO, not when performing a write operation
when it comes to queries just query the database, why load a behavioral object to show data on a screen?
You should modify your domain objects only when you need that change for write operations.
If queries become too complex thats another sign that they are two different concerns
ROME 11-12 april 2014 - Forat Latif
Back to the User Dashboard
ROME 11-12 april 2014 - Forat Latif
User Dashboard with CQRS
class UserDashboardProjector {handle(ProductAdded productAdded) {
//insert into “user dashboard entry” with the data in the event
}
handle(ProductShipped productShipped) {//update “user dashboard entry” with the data in the
event }
}class ProductQueryProcessor {List<UserDashboardEntryDTO> getProducts(String
userId) {//Select from “user dashboard entry” where
userId = userId}
}
ROME 11-12 april 2014 - Forat Latif
Category Tree with CQRS
ROME 11-12 april 2014 - Forat Latif
Category Tree with CQRS
Books
Technical
Accademic
DDD Blue Book
Some Weird Book
Category
Book BookCategory
ROME 11-12 april 2014 - Forat Latif
Category Tree with CQRSclass CategoryProjector {
handle(CategoryAdded categoryAdded) {Vertex child = createVertex();child.setProperty(“name”,
categoryAdded.name());Vertex parent =
getVertex(categoryAdded.parentId());parent.addEdge(child);
}}class ProductQueryProcessor {
List<ProductDTO> getProducts(String categoryId) {// graph query to get all children of type x;
}}
ROME 11-12 april 2014 - Forat Latif
Multiple categories
ROME 11-12 april 2014 - Forat Latif
Multiple categories
Without CQRS:
Nested Query : ….
Modify the domain (even more Impedance mismatch pain)
Mixed: (join table unavoidable)
ROME 11-12 april 2014 - Forat Latif
Multiple categories
With CQRS:
class Product {List<Categories> categories;
}
and … thats it! The read model doesnt need to be modified
ROME 11-12 april 2014 - Forat Latif
Notice a pattern?
Read model is data driven
Write model is Domain Driven (database not important)
To design your write model you can imagine that you dont need a database (you have infinite persisant RAM)
ROME 11-12 april 2014 - Forat Latif
Aggregates as Documents- consistency easier to maintain,
- performance
- scalability
- technical simplicity (no lazy load config nor impedence mismatch)
class Product {String productId;
@EmbedSomehow // hopefully with a doc databaseList<String> categoryIds;
}
ROME 11-12 april 2014 - Forat Latif
Commandsclass OrderService {
@Transactionalpublic void purchaseOrder(String orderId) {
……}
}
class PurchaseOrderCommandHandler {@Transactionalpublic void handle(PurchaseOrderCommand command) {
……}
}
ROME 11-12 april 2014 - Forat Latif
Advantages of Commands
Command log (not your regular log)
Clear user intention
Improved Testability
Integration
Get rid of AOP
Command Sourcing
ROME 11-12 april 2014 - Forat Latif
Advantages of Commands
Represent an intention, which usually maps to a command method in your aggregate, which causes one or many events to be pushed.
class ChangeCustomerTelephoneCommandHandler {@Transactionalpublic void handle(ChangeCustomerTelephoneCommand command)
{……
}}
ROME 11-12 april 2014 - Forat Latif
Overview of CQRS
ROME 11-12 april 2014 - Forat Latif
Recap of CQRS
CQS is a principle that can be applied at the architecturale level.
Read and Write are two separate concerns
You cannot solve all problems with the same model, especially if the problem domains have different points of view
ROME 11-12 april 2014ROME 11-12 april 2014
Moving Away From CRUD: Domain Driven Design (DDD) and Command Query Responsibility Segregation (CQRS)
Forat LatifEmail: [email protected]: @foratlatif
Questions?