Introducing 

Prezi AI.

Your new presentation assistant.

Refine, enhance, and tailor your content, source relevant images, and edit visuals quicker than ever before.

Loading…
Transcript

Standard (main) flow

Event based flow

Use arrows at the bottom to watch the "story"

You can also zoom (mouse wheel) and move (holding left button) to watch details freely

Association

Notice that links points to source code in repo: http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample

Browser + AJAX

Web Service Client

SWT (Remoting)

Browser

Android

Serializes Commands to JSON

Not important, all interesing stuff happends in Controler - Presentation Layer in Server Tier

Sends Commands to the Server Gate

- mobile client can be occasionally connected

- so it can queue commands (those which are annotated as offlineSupport=true) if there is no connection

- when connection is established it sends queued commands

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fwebapp

Client Tier

Send Commands to the Gate

Sales Module (Bounded Context)

CRUD Use Cases

CRM Module (Bounded Context)

Shipment Module (Bounded Context)

pl.com.bottega.erp.sales.webui

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fsales%2Fwebui

Presentation Layer (Web MVC)

pl.com.bottega.erp.sales

Controler

Web MVC Controlers

pl.com.bottega.erp.crm

pl.com.bottega.erp.shipping

It's always handfull and productive

to use some CRUD Framework

for simple Use Cases (ex. Supporting Domains)

a) classic way: calls Application Serivce

b) cqrs way: creates Command and sends it to the Gate

Client knows if he is interested in immediate Command Execution. If not, than You annotate Command with asynchronous=true parameter

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fsales

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fcrm

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fshipping

classic way: call Application Service

xxxDTO

xxxSearchCiteria

You can annotate Command classes with pl.com.bottega.cqrs.command.Command in order to introduce:

- duplication checking

- asynchronous handling

pl.com.bottega.erp.sales.application.commands

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fsales%2Fapplication%2Fcommands

<<Command>>

CreateOrderCommand

SubmitOrderCommand

Contains list of product IDs

Contains order ID

May contain for example:

- payment

- shipment address

- ect

Business (Write) Model

Presentation (Read) Model

pl.com.bottega.cqrs.command

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Fcqrs%2Fcommand

pl.com.bottega.cqrs.command.Gate

pl.com.bottega.cqrs.command.RunEnvironment

Gate

Run Environment

pl.com.bottega.cqrs.command.handler.CommandHandler

Queue

CommandHandler

Gate History

Base interface for Handlers, specyfying two generic types:

- C - command type

- R - result type (should be Void for asynch. commands)

Asynch Command

Stores X last commands that were ran (commands that are annotated as "unique").

equals() method decides if commands are the same

For example: approving order with the same ID twice does not make any sense.

Warning: History is just an optimisation - You must check rules also in domain model

Main Responsibility:

- find relevant Command Handler and run it passing given Command

This is also a perfect place to introduce additional features before and after running a Handler:

- injecting dependencies into a Command handler (if You don't want to use IoC container like Spring IoC)

- handle transactions, security ect. (if You don't want to use AOP techniques like Spring AOP)

- profiling

- logging

- syping:)

We suggest to annotate each Handler with: pl.com.bottega.cqrs.command.handler.CommandHandlerAnnotation

How does RunEnvironment find relevant Handler?

Resposibility:

- main gate to the Server

- optimisation such as: rejecting command duplicates, dispatching asynch. commands to the queue

1. If Command is annotaed as "unique" than checks in History if the given command was already ran

- If so, than reject Command

2. If Command is annotated as "asynchronous" than sends given command to the Queue and returns

3. Sends Command to the Run Environment

RunEnvironment delegats this problem to the HandlersProvider

You can implement Your own Provider

You can use our solution

pl.com.bottega.cqrs.command.handler.spring.SpringHandlersProvider

In this Sample all Handlers are managed by Spring, so we rely on Spring's Dependency Injection, Transaction managment and Security

Simplest way is to use HashMap:

- key is a Command class

- value is a CommandHandler

1. This handler scans all Spring Beans

2. If bean implements CommandHandler interface than that's our guy

3. We check Generic type of that class - type of handled Command

4. We simply register this information in a hashmap

- key is a Command class

- value is a Spring Bean name

Synch (standard) Command

pl.com.bottega.erp.sales.presentation

pl.com.bottega.erp.sales.application

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fsales%2Fapplication

Application Layer

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fsales%2Fpresentation

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fsales%2Fapplication%2Fcommands%2Fhandlers

Classic approach

alternative for Commands

pl.com.bottega.erp.sales.application.commands.handlers

Command Handler responsibility: orchestrate Domain Objects to fullfill part of the UseCase/UserStory

CH contains needed dependencies (ex. Repositories, ) that are Injected by:

- IoC Container (ex. Spring) - if CH is managed by some container

- RunEnvironment - if You don't want to inroduce any container

SubmitOrderCommandHandler

pl.com.bottega.erp.sales.application.listeners

pl.com.bottega.erp.sales.application.services

CreateOrderCommandHandler

Application Services

Application Listeners

Thin Read Layer

SCENARIO

Handles Events from another Bounded Contexts (modules)

- used to decouple modules

- can be considered as an Anticorruption Layer or Context Mapper

This kind of Listeners can issue a command to this Bounded Context (Module) or simply perform some logic just like CommandHandlers do.

App Service has the same responsibility as a Command Handler: orchestrate Domain Objects to fullfill part of the UseCase/UserStory

AS may use AOP to assure Security, transactions, ect.

  • Creates Order using OrderFactor
  • Adds Products to the Order
  • Fires Application Event
  • Saves Order using OrderRepository
  • Loads Order using OrderRepository
  • Checks OrderSpecification
  • Calls submitMethod
  • Generates Invoice using Invoicing Business Service
  • Saves Order and Invoice using Repositories

<<Fire Event>>

CustomerStatusChangedListener

PurchaseApplicationService

Functionality similar to Handlers

If a customer becomes a VIP then calculate x% rebate for all his orders that are in DRAFT status

This logic belongs to the Sales Bounded Context, but is triggered form CRM Bounded Context via Domain Event

Statefull Application Objects

<<ApplicationEvent>>

Use App Event when

pl.com.bottega.erp.sales.application.events

ProductAddedToOrderEvent

  • Something important happend in Application (but not in domain model)
  • Events that are specific for some UserStory/UseCase

Sample Finders

Sample objects that are obly concepts of Application (model of some Application Features).

pl.com.bottega.ddd.application

ClientBasket

SystemUser

contains info about

- cleint ID

- product ID

- quantity

lets assume that we want to spy clients only in this Use Case

  • Session Scoped Bean
  • Represents curently logged in user
  • Caontains ID of the User Entity
  • It is convinient to inject this object into CommandHandlers/ApplicationServiced if You need to know who id performing current operation

An application concept introduced to model convenient feature of the Application. In this Sample We assume that this in not a Domain concept (but in some business domain it may be).

  • Using SQL or JPA: select new DTO()
  • Reading Business Model, Views or Dedicated Models

App Server Tier

Application Layer is a "scenario" to orchiestrate domain "actors"

pl.com.bottega.erp.crm.domain

pl.com.bottega.erp.sales.domain.specification.order

Domain Layer

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fcrm%2Fdomain

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fsales%2Fdomain

Domain Layer

<<DomainPolicy>>

<<DomainFactory>>

RebatePolicy

RebatePolicyFactory

<<create>>

Encapsulates Business Logic that determines which impl should be used.

Uses injected SystemUser to make this decission.

  • Simply a Strategy Design Pattern
  • Encapsulates variations of the rebate calculations
  • Implemetations can be decorated using Decorator Design Pattern

<<transformer>>

<<DomainPolicyImpl>>

<<Decorator>>

VipRebate

<<DomainPolicyImpl>>

StandardRebate

Customer

Mapped in JPA as @Embedable no need to JOIN

<<DomainService>>

<<ValueObject>>

<<delegate>>

Money

Encapsulates representation (BigDecimal or shifted int) and Currency

<<DomainEvent>>

InvoicingService

<<DomainFactory>>

<<DomainAggregateRoot>>

OrderFactory

<<DomainFactory>>

<<DomainAggregateRoot>>

InvoiceFactory

Invoice

Order

OrderShippedEvent

<<creates>>

  • Makes sure that created Aggreate is in valid state
  • Performs Dependency Injection to Order (using InjectorHelper to perform common injection)
  • Encapsulates Business Logic (assembling Aggreate): calculaating RebatePolicy, adding Gratis Products, ect.

Encapsulates Domain Logic which does not belong to any Aggreagte

We don't want to have generateInvoice() method in Order class bacause We don't want Order to become a 'God Class'

private Money net

private Money gros

private List<InvoiceLine> items;

In this Bounded Context Customer is something different than a Client in Sales

Notice hiding OrderLine

private Money totalCost;

private List<OrderLine> items;

public void addProduct(Product, quantity)

public void submit()

public List<OrderedProduct> getOrderedProducts()

This is a Transformer in terms of Responsibility Driven Design - transorms Order to Invoice

<<ValueObject>>

<<DomainFactory>>

OrderedProduct

InvoiceRepository

Introduced to encapsulate inner Aggreate Impl - in particural OrderLine model which is assumed to be unstable in this moment of time

<<Entity>>

<<Entity>>

Projection of the Inner Model

<<delegates>>

<<DomainFactory>>

InvoiceLine

OrderLine

OrderRepository

Encapsulated concept.

We assume that this model is not stable (will change), so we make a "projction" using ValueObjects

<<load/save>>

  • load/save
  • Performs Dependency Injection to Order (using InjectorHelper to perform common injection)

<<DomainPolicy>>

TaxPolicy

  • Simply a Strategy Design Pattern
  • Encapsulates variations of the tax calculations

public void changeStatus(Status newStatus)

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fshipping%2Fdomain%2Fevents

<<fires>>

<<DomainEvent>>

OrderSubmittedEvent

<<DomainEvent>>

ShipmentDeliveredEvent

<<Fire Event>>

pl.com.bottega.erp.sales.domain.specification.order

Specification

Encapsulates Tree of Rules that determine if given Order can be submitted

<<Fire Event>>

<<DomainEvent>>

CustomerStatusChangedEvent

Contains:

- customerId

- status

pl.com.bottega.erp.sales.infrastructure

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fsales%2Finfrastructure

Infrastructure Layer

pl.com.bottega.erp.sales.infrastructure.events.listeners.application

pl.com.bottega.erp.sales.infrastructure.repositories.jpa

pl.com.bottega.erp.sales.infrastructure.events.listeners.domain

Repositories Impl

Domain Events Listeners

Application Events Listeners

Sample Repositories

  • handles deletion as status change
  • performs dependency injection

Can perform additional logic (ex. sending email)

  • plugin oriented architecture
  • asynchronous execution of the long running operations
  • decoupling
  • store Events in Event Store (audit log)

Can perform additional logic (ex. sending email)

  • plugin oriented architecture
  • asynchronous execution of the long running operations
  • decoupling
  • updating presentation model
  • store Events in Event Store (audit log, Event Sourcing)

Events Engine

Saga Engine

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Fddd%2Fsagas

pl.com.bottega.erp.sales.saga

pl.com.bottega.ddd.sagas

Saga

Saga API

pl.com.bottega.ddd.infrastructure.events

  • interface for Saga and its Data Loader
  • annotations for Sagas, event listener methods and loading methods

Events

Saga is a:

  • persistent (Saga state is stored betweent calls)
  • multi-listener (Saga handles many events)

model of complex (distributed) Business Process

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Fddd%2Finfrastructure%2Fevents

<<Saga>>

OrderShipmentStatusTrackerSagaManager

OrderShipmentStatusTrackerSaga

  • Tries to find Saga Memento that matches to given Event
  • If no Memento found (saga just starts) than creates brand new Memento
  • Orchiestrates multi-event process
  • Contains many listener methods
  • Each method changes Saga state and tries to end saga (if business condtion is fullfilled)

<<find/create>>

OrderShipmentStatusTrackerData

  • Simply a Memento Design Pattern
  • Persistent state of the Saga

Impl of Events Engine.

Here is how it works:

pl.com.bottega.ddd.infrastructure.events.impl.EventListenerBeanPostProcessor

scans all Spring Beans.

If bean contains metohod annotated @EventListener than that's our guy. We simply check parameter of this method and register it in:

pl.com.bottega.ddd.infrastructure.events.impl.SimpleEventPublisher

which is both Domain and Application Event publisher.

So when You fire an event You actually speak to this guy.

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fsales%2Fsaga

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Fddd%2Finfrastructure%2Fsagas

pl.com.bottega.ddd.infrastructure.sagas

<<listener>>

Saga impl

EventListener annotation

Impl of Saga Engine.

Sagas engine is simply one of the Event listeners.

At system start Saga Engine scans all Saga Beans and finds out which Events they are interested in.

For each event Saga Engine registeres itself as an listener in Events Engine.

Use

pl.com.bottega.ddd.infrastructure.events.EventListener

to annotate listener methods

Notice that a Listener (not an event) can express intention that its logic can be performed in asynchronous way - listner knows what it is doing.

It is opposite way as in case of Commands where client sending a Command could express asynchronous intention - it was ouside of the Handler knowledge scope.

Business (Write) Model

Presentation (Read) Model

A) Flatten while reading

  • Pure SQL + DTO
  • "select new DTO(...)" technique in JPA

Event Sourcing

Relational DB

Stores Domain Events

OR

Flatten to First Normal Form

  • 3rd Normal Form
  • Good for storing Entities and Aggreagtes (associations)
  • Manipulating Aggregates using JPA (Lazy Loading + Casacde Operations)
  • but loading cross cutting data can be painfull (unnecesary data, n+1 Select Problem)

B) Flatten using Views

  • First Normal Form - think what You query for
  • Materialized Views

DB Tier

Event Listeners

C) Flatten using dedicated read models

  • You can have many "perspectives" to look at domain model
  • Just design presntation model and add another Listener that updates this model based on ovent form Business Stack

You can use asynch Listners (and queue) if You don't need to update Presentation Model immediately

pl.com.bottega.erp.sales.presentation.listeners

ProductEventsListener

http://code.google.com/p/ddd-cqrs-sample/source/browse/#svn%2Ftrunk%2Fddd_cqrs-sample%2Fsrc%2Fmain%2Fjava%2Fpl%2Fcom%2Fbottega%2Ferp%2Fsales%2Fpresentation%2Flisteners

  • First Normal Form - think what You query for
  • Dedicated model for reading
  • Updated by Events
  • You can have many models (perspectives) designed per problem
  • You can scale them almost linearly in cost :)
Learn more about creating dynamic, engaging presentations with Prezi