Top Banner
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
57

Ddd cqrs - Forat Latif

Sep 07, 2014

Download

Technology

Codemotion

Le slide di Forat Latif presentate a Codemotion Roma 2014
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Ddd cqrs - Forat Latif

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

Page 2: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Speaker’s name

Time to start a greenfield project

Page 3: Ddd cqrs - Forat Latif

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

Page 4: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Speaker’s name

What the Architecture looks like?

Page 5: Ddd cqrs - Forat Latif

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

Page 6: Ddd cqrs - Forat Latif

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() { …….

……}

Page 7: Ddd cqrs - Forat Latif

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

}}

Page 8: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Speaker’s name

So … Where is the business logic?class ProductController {

public void updateProduct(Product product) {productRepository.save(product);

}}

Page 9: Ddd cqrs - Forat Latif

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() { …….

……}

Page 10: Ddd cqrs - Forat Latif

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

}}

Page 11: Ddd cqrs - Forat Latif

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

}}

Page 12: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Speaker’s name

Back to our “Entities”

class Customer {String name;Address address;

}

class Address {String street;Integer streetNumber;

}

Page 13: Ddd cqrs - Forat Latif

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

Page 14: Ddd cqrs - Forat Latif

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

}

Page 15: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Speaker’s name

Suddenly our mail server goes down …

Page 16: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Domain Events

A Domain Event, represents something that already happened, that is relevant to the business

Page 17: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Domain Events

class Service {

void handle(SomeEvent event) {doSomething();

}

}

Page 18: Ddd cqrs - Forat Latif

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

}

}

Page 19: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Sales of a Product skyrocket ….

Page 20: Ddd cqrs - Forat Latif

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

}

Page 21: Ddd cqrs - Forat Latif

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

Page 22: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

What our new Architecture looks like

Page 23: Ddd cqrs - Forat Latif

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;

}

Page 24: Ddd cqrs - Forat Latif

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

Page 25: Ddd cqrs - Forat Latif

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

Page 26: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Communication between BCs

Inventory

E-commerce

Shipping

Page 27: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

How to identify BCs

Ubiquitous Language

Goals and Concerns

Teams

Organizational Structure

Page 28: Ddd cqrs - Forat Latif

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

Page 29: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

What about User Interfaces?

class ProductController {

Product viewProduct (String productId) {Product product =

productRepository.find(productId);return product;

}

}

Page 30: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

User dashboard

Page 31: Ddd cqrs - Forat Latif

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

}

Page 32: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

List the products of a category

Page 33: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Category Tree

Page 34: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Category Tree Model

class Category {String name;Category parent;

}

class Product{Category category;

}

Page 35: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

“Simple” change request …

Page 36: Ddd cqrs - Forat Latif

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

Page 37: Ddd cqrs - Forat Latif

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;

}

Page 38: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Possible Solutions

Another possibility:

class Category {String parent1Id;String parent2Id;String parent3Id;

….

String categoryId;String name;

}

Page 39: Ddd cqrs - Forat Latif

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

Page 40: Ddd cqrs - Forat Latif

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

}

Page 41: Ddd cqrs - Forat Latif

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

Page 42: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Back to the User Dashboard

Page 43: Ddd cqrs - Forat Latif

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}

}

Page 44: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Category Tree with CQRS

Page 45: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Category Tree with CQRS

Books

Technical

Accademic

DDD Blue Book

Some Weird Book

Category

Book BookCategory

Page 46: Ddd cqrs - Forat Latif

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;

}}

Page 47: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Multiple categories

Page 48: Ddd cqrs - Forat Latif

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)

Page 49: Ddd cqrs - Forat Latif

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

Page 50: Ddd cqrs - Forat Latif

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)

Page 51: Ddd cqrs - Forat Latif

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;

}

Page 52: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Commandsclass OrderService {

@Transactionalpublic void purchaseOrder(String orderId) {

……}

}

class PurchaseOrderCommandHandler {@Transactionalpublic void handle(PurchaseOrderCommand command) {

……}

}

Page 53: Ddd cqrs - Forat Latif

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

Page 54: Ddd cqrs - Forat Latif

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)

{……

}}

Page 55: Ddd cqrs - Forat Latif

ROME 11-12 april 2014 - Forat Latif

Overview of CQRS

Page 56: Ddd cqrs - Forat Latif

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

Page 57: Ddd cqrs - Forat Latif

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?