Slide 1

Slide 1 text

F R O M U S E C A S E A R C H I T E C T U R E T O E V E N T S O U R C I N G T H R O U G H C Q R S W I T H S O M E D D D I N I T. T H E B U Z Z W O R D C O N F E R E N C E

Slide 2

Slide 2 text

@ L I L O B A S E – L G O . G R O U P Arnaud LEMAIRE | LGO

Slide 3

Slide 3 text

VS DATA-DRIVEN DOMAIN-DRIVEN WHAT DATA ? WHAT BEHAVIOR ?

Slide 4

Slide 4 text

VS UPDATE ADDRESS MISTAKE CORRECTION RELOCATION CRUD ARCHITECTURE TASK BASED UI STAND FOR USER INTERACTION USER INTENT IS LOST :-(

Slide 5

Slide 5 text

SO, HOW DO WE START ? SubscriptionController UserController DashboardController SessionController MCD CONTROLLERS

Slide 6

Slide 6 text

MoveHub AddMaintenanceOperation TestLighting CommissionHub ENTITIES USE CASES Hub MaintenanceOperation TestPlan Commissioning YOU DON’T NEED TO HAVE THE FULL CORRECT LISTS, JUST ENOUGH TO GET STARTED HOW DO WE START ?

Slide 7

Slide 7 text

Line Invoice PRODUCT Line Line PRODUCT PRODUCT INVENTORY CATEGORY PRODUCT CATEGORY CATEGORY PRODUCT PRODUCT

Slide 8

Slide 8 text

Line Invoice PRODUCT Line Line PRODUCT PRODUCT INVENTORY CATEGORY CATEGORY CATEGORY

Slide 9

Slide 9 text

IL Y A PLUSIEURS METIERS DANS UNE APPLICATION ET VOUS NE DEVEZ PAS LES COUPLER

Slide 10

Slide 10 text

U S E C A S E & A G G R E G AT E R O O T A N I N T R O D U C T I O N

Slide 11

Slide 11 text

ENTITIES & VALUE OBJECTS DateTime Money BillingInterval ENTITIES VALUE OBJECTS Hub Maintenance Operation Session THE DOMAIN IS EXPRESS THROUGH PLAIN OLD PHP OBJECTS CATEGORIZED BETWEEN ENTITIES & VALUE OBJECTS EQUALITY ON IDENTITY, HAS LIFECYCLE EQUALITY ON VALUES, HAS NO LIFECYCLE

Slide 12

Slide 12 text

AGGREGATE ROOT HUB MAINTENANCE OPERATIONS PARTS LOCATION ID OPERATIONS CAN ONLY BE PERFORMED THROUGH THE ROOT ENFORCE INTEGRITY REFERENCE TO COLLABORATOR WITH ≠ LIFECYCLE AGGREGATE OBJECT’S SHARE A COMMON LIFECYCLE: THEY CAN BE SAVED & FETCHED AS A WHOLE

Slide 13

Slide 13 text

A G G R E G AT E R O O T A R E B U S I N E S S O B J E C T public class Hub implements AggregateRootWithUUID { static public Hub create(
 String productReference, UUID locationId ) { Hub instance = new Hub(UUID.randomUUID()); instance.locationId = locationId; instance.productReference = productReference; return instance; } private Hub(UUID id){ this.id = id; //We control the ID generation } @Override public UUID id() { return id; } }

Slide 14

Slide 14 text

T H AT C O N TA I N C O L L A B O R AT O R S H U B M A I N T E N A N C E O P E R AT I O N M A I N T E N A N C E O P E R AT I O N M A I N T E N A N C E O P E R AT I O N public class MaintenanceOperation { MaintenanceOperation( String description, Type operationType, LocalDateTime dateTime ) { this.description = description; this.operationType = operationType; this.dateTime = dateTime; } }

Slide 15

Slide 15 text

T H AT P R O T E C T C O N S I S T E N C Y public class Hub implements AggregateRootWithUUID { //… public void addMaintenanceOperation(
 String description, 
 MaintenanceOperation.Type type
 ) { maintenanceOperations =
 maintenanceOperations.append( new MaintenanceOperation( description, type, LocalDateTime.now())); } }

Slide 16

Slide 16 text

A N D C A N H AV E R E F E R E N C E T O O T H E R A G G R E G AT E public class Hub implements AggregateRootWithUUID { static public Hub create(
 String productReference, UUID locationId ) { final Hub instance = new Hub(UUID.randomUUID()); instance.locationId = locationId; instance.productReference = productReference; return instance; } private UUID locationId; }

Slide 17

Slide 17 text

U S E C A S E A R E D T O public class NewMaintenanceOperationUseCase implements UseCase { public NewMaintenanceOperationUseCase( String switchId, String description, String type ) { this.switchId = switchId; this.description = description; this.type = type; } public String switchId; public String description; public String type; }

Slide 18

Slide 18 text

W I T H A D E D I C AT E D H A N D L E R public class NewMaintenanceOperationUseCaseHandler implements UseCaseHandler { public NewMaintenanceOperationUseCaseHandler( SwitchRepository repository ) { this.repository = repository; } @Override public void handle(NewMaintenanceOperationUseCase command) { Hub hub = repository.get(UUID.fromString(command.switchId)); Type maintenanceOperationType = Type.valueOf(command.type); hub.addMaintenanceOperation(
 command.description, 
 maintenanceOperationType ); repository.add(aSwitch); } private final SwitchRepository repository; }

Slide 19

Slide 19 text

C Q R S C O M M A N D Q U E RY R E S P O N S I B I L I T Y S E G R E G AT I O N

Slide 20

Slide 20 text

C H A N G E S O M E T H I N G E V E RY T H I N G S TA R T S W I T H U S E C A S E S U S E R I want to … G E T I N F O A B O U T S O M E T H I N G (read) (mutation) C O M M A N D Q U E RY « I need information to make a decision » « I made a decision »

Slide 21

Slide 21 text

C O M M A N D S & Q U E R I E S A R E D T O class RelocateHubCommand implements Command {
 
 public RelocateHubCommand(
 String hubId, String newLocationId ) {
 this.hubId = hubId;
 this.newLocationId = newLocationId;
 }
 
 @NotEmpty
 public String hubId;
 
 @NotEmpty
 public String newLocationId;
 
 } A command returns no business data to the client (can return a status & an id)

Slide 22

Slide 22 text

S E N T I N A B U S @RestController
 class HubResource {
 
 @Autowired
 public HubResource(CommandBus commandBus) {
 this.commandBus = commandBus;
 }
 
 @PutMapping("/api/hub/location")
 public Void relocate(Request request) {
 Map requestBody = Serializer.deserialize(request.body()); 
 return this.commandBus.dispatch( new RelocateHubCommand( requestBody.get("hubId"), requestBody.get("newLocationId") ));
 }
 }

Slide 23

Slide 23 text

S E N T I N A B U S @RestController
 class HubResource {
 
 @Autowired
 public HubResource(CommandBus commandBus) {
 this.commandBus = commandBus;
 }
 
 @PutMapping("/api/hub/location")
 public Void relocate(Request request) {
 Map requestBody = Serializer.deserialize(request.body()); 
 return this.commandBus.dispatch( new RelocateHubCommand( requestBody.get("hubId"), requestBody.get("newLocationId") ));
 }
 } Almost no stickiness to a framework

Slide 24

Slide 24 text

D I S PAT C H E D T O A S P E C I F I C H A N D L E R @Component
 public class CommandBus {
 
 @Autowired
 public CommandBus(List extends Handler> handlers) {
 this.handlers = handlers.toMap( h -> Tuple.of(h.listenTo(), h) );
 }
 
 public > R dispatch(C command) {
 return handlers .get(command.getClass()) .handle(command);
 }
 
 private final Map handlers;
 }

Slide 25

Slide 25 text

D I S PAT C H E D T O A S P E C I F I C H A N D L E R @Component
 public class CommandBus {
 
 @Autowired
 public CommandBus(List extends Handler> handlers) {
 this.handlers = handlers.toMap( h -> Tuple.of(h.listenTo(), h) );
 }
 
 public > R dispatch(C command) {
 return handlers .get(command.getClass()) .handle(command);
 }
 
 private final Map handlers;
 }

Slide 26

Slide 26 text

T H E F U L L P I P E L I N E U S E R C O M M A N D Q U E RY Q U E RY H A N D L E R C O M M A N D H A N D L E R one per query one per command Q U E RY B U S C O M M A N D B U S

Slide 27

Slide 27 text

M I D D L E WA R E @Component public class BusValidationMiddleware implements BusMiddleware> { @Autowired
 public BusValidationMiddleware( BusMiddleware next, Validator validator ) {
 this.next = next;
 this.validator = validator;
 }
 
 public T handle(Message message) {
 Violation violations = validator.validate(message);
 if(violations.size() > 1)
 throw new ArgumentException(violations.toArray());
 
 return next.handle(message);
 }
 }

Slide 28

Slide 28 text

C O M P O S E D I N T H E B U S U S E R C O M M A N D Q U E RY new BusValidationMiddleware(
 new BusTransactionMiddleware(
 new BusDispatcher( Q U E RY H A N D L E R C O M M A N D H A N D L E R Q U E RY B U S C O M M A N D B U S VA L I D AT I O N C A C H E VA L I D AT I O N U N I T O F W O R K

Slide 29

Slide 29 text

C Q R S T H E C O M M A N D

Slide 30

Slide 30 text

C O M M A N D H A N D L I N G @Component
 public class RelocateHubCommandHandler implements CommandHandler {
 @Autowired
 public RelocateHubCommandHandler(HubRepository repo) {
 this.repository = repo;
 }
 
 @Override
 public Void handle(RelocateHubCommand command) {
 Hub hub = repository.getSiteInformation();
 hub.relocateTo(command.newLocationId);
 }
 
 public Class listenTo() {
 return RelocateHubCommand.class;
 }
 }

Slide 31

Slide 31 text

C O M M A N D H A N D L I N G @Component
 public class RelocateHubCommandHandler implements CommandHandler {
 @Autowired
 public RelocateHubCommandHandler(HubRepository repo) {
 this.repository = repo;
 }
 
 @Override
 public Void handle(RelocateHubCommand command) {
 Hub hub = repository.get(command.hubId);
 hub.relocateTo(command.newLocationId);
 }
 
 public Class listenTo() {
 return RelocateHubCommand.class;
 }
 }

Slide 32

Slide 32 text

H A N D L E R S W O R K O N A G G R E G AT E public class Hub {
 public Hub(UUID id) { this.id = id; }
 
 public Hub() { this.id = UUID.randomUUID(); }
 
 public UUID id() { return id; } 
 //...
 
 public void relocateTo(String newLocationId) {
 locationId = UUID.fromString(newLocationId);
 }
 
 //...
 
 private UUID locationId;
 private UUID id;
 } We keep under control the aggregate id creation

Slide 33

Slide 33 text

P E R S I S T E N C E A B S T R A C T I O N public interface Repository {
 TRoot get(TId id);
 
 void add(TRoot root);
 
 void delete(TRoot root);
 
 List getAll();
 } The persistence mechanism save snapshot of the application state

Slide 34

Slide 34 text

P E R S I S T E N C E A B S T R A C T I O N ( P S E U D O M A P I N T E R FA C E ) public class HubRepository implements Repository {
 
 public HubRepository(Store store)
 this.store = store;
 
 public Hub get(UUID uuid)
 return store.findById(uuid);
 
 public void add(Hub hub)
 store.upsert(hub);
 
 public void delete(Hub hub)
 store.remove(hub);
 
 public Seq getAll()
 return store.findAll();
 } We fetch and save aggregate as a whole

Slide 35

Slide 35 text

H A N D L E R I N T E R N A L S S T O R E D B A G G R E G AT E S T O R E D B fetched flushed by the unit of work middleware domain operations handler(state, command) = state’ saved

Slide 36

Slide 36 text

T E S T I N G @Test
 public void it_relocates_a_hub() {
 store = new InMemoryStore();
 command = new RelocateHubCommand("hubId", "newLocationId");
 handler = new RelocateHubCommandHander( new HubRepository(store) );
 
 handler.handle(command); 
 expectedHub = store.get("hubId");
 assertThat(expectedHub.location()).equals("newLocationId");
 }

Slide 37

Slide 37 text

C Q R S T H E Q U E RY

Slide 38

Slide 38 text

C O M M A N D S & Q U E R I E S A R E D T O class FindHubByLocationPathQuery implements Query {
 
 public FindHubByLocationPath(
 String locationPath, ) {
 this.locationPath = locationPath;
 }
 
 @NotEmpty
 public String locationPath;
 
 }

Slide 39

Slide 39 text

Q U E RY H A N D L I N G @Component
 public class FindHubByLocationPathQueryHander extends SqlHandler {
 
 @Autowired
 public FindHubByLocationPathQueryHander(SqlConnection sql) {
 this.sql = sql;
 }
 
 @Override
 public Installation handle(FetchAllLocationsQuery query) {
 Record hub = sql.prepare( "SELECT * FROM hub JOIN location ON hub.locationId = location.id WHERE location.path = %s" ).execute(query.locationPath);
 return Serializer.deserialize(hub);
 }
 } We receive a direct connection to the datastore

Slide 40

Slide 40 text

Q U E RY H A N D L I N G @Component
 public class FindHubByLocationPathQueryHander extends SqlHandler {
 
 @Autowired
 public FindHubByLocationPathQueryHander(SqlConnection sql) {
 this.sql = sql;
 }
 
 @Override
 public Installation handle(FetchAllLocationsQuery query) {
 Record hub = sql.prepare( "SELECT * FROM hub JOIN location ON hub.locationId = location.id WHERE location.path = %s" ).execute(query.locationPath);
 return Serializer.deserialize(hub);
 }
 }

Slide 41

Slide 41 text

C Q R S W R A P P I N G U P

Slide 42

Slide 42 text

T H E S A M E D ATA S T O R E C A N B E S H A R E D U S E R C O M M A N D Q U E RY Q U E RY H A N D L E R C O M M A N D H A N D L E R P E R S I S T E N C E while maintaining a complete segregation through the whole application Q U E RY B U S C O M M A N D B U S

Slide 43

Slide 43 text

TA K E A WAY • Massive simplification of persistence mechanisms • Unit testing in a breeze • Enable business use case discovery • Drive task based UI instead of CRUD thinking

Slide 44

Slide 44 text

D O M A I N E V E N T S T I L L N O T E V E N T S O U R C I N G

Slide 45

Slide 45 text

E V E N T ≠ C O M M A N D C O M M A N D E V E N T Intent : « do something » Event : « something happened »

Slide 46

Slide 46 text

E V E N T A R E A L S O D T O class HubMovedEvent implements Event {
 
 public HubMovedEvent(
 String hubId, String oldLocationId, String newLocationId ) {
 this.hubId = hubId; this.oldLocationId = oldLocationId;
 this.newLocationId = newLocationId;
 }
 
 public String hubId;
 
 public String newLocationId; public String oldLocationId;
 }

Slide 47

Slide 47 text

A N D A L S O S E N T I N A B U S eventBus.dispatch( new HubMoved( "hubId", "oldLocationId", "newLocationId" ));

Slide 48

Slide 48 text

B U T W I T H M U LT I P L E H A N D L E R public EventBus(List extends EventHandler> handlers) {
 this.handlers = handlers.groupBy( EventHandler::listenTo()
 ));
 }
 
 public Void dispatch(C event) {
 handlers.get(event.getClass())
 .forEach(h -> h.handle(event));
 }

Slide 49

Slide 49 text

A N E V E N T I S T H E R E S U LT O F A B U S I N E S S O P E R AT I O N public class Hub {
 
 public Event relocateTo(String newLocationId) {
 HubMovedEvent event = new HubMovedEvent(
 this.locationId.toString(),
 newLocationId
 );
 
 this.locationId = UUID.fromString(newLocationId);
 
 return event;
 }
 UUID locationId;
 private UUID id;
 }

Slide 50

Slide 50 text

R E T U R N E D B Y C O M M A N D H A N D L E R S @Component
 public class RelocateHubCommandHandler implements CommandHandler {
 @Autowired
 public RelocateHubCommandHandler(HubRepository repo) {
 this.repository = repo;
 }
 
 @Override
 public Void handle(RelocateHubCommand command) {
 Hub hub = repository.get(command.hubId);
 hub.relocateTo(command.newLocationId);
 }
 
 public Class listenTo() {
 return RelocateHubCommand.class;
 }
 }

Slide 51

Slide 51 text

R E T U R N E D B Y C O M M A N D H A N D L E R S @Component
 public class RelocateHubCommandHandler implements CommandHandler {
 @Autowired
 public RelocateHubCommandHandler(HubRepository repo) {
 this.repository = repo;
 }
 
 @Override
 public Tuple2 handle(RHC command) {
 Hub hub = repository.get(command.hubId);
 return Tuple.of(
 void,
 List.of(hub.relocateTo(command.newLocationId)
 );
 }
 
 public Class listenTo() {
 return RelocateHubCommand.class;
 }
 }

Slide 52

Slide 52 text

D I S PAT C H E D B Y T H E C O M M A N D B U S @Component
 public class CommandBus {
 
 @Autowired
 public CommandBus(List extends Handler> handlers) {
 this.handlers = handlers.toMap( h -> Tuple.of(h.listenTo(), h) );
 }
 
 public > R dispatch(C command) {
 return handlers .get(command.getClass()) .handle(command);
 }
 
 private final Map handlers;
 }

Slide 53

Slide 53 text

D I S PAT C H E D B Y T H E C O M M A N D B U S @Component public class CommandBus { @Autowired public CommandBus(
 Collection extends CommandHandler> handlers, EventBus eventBus) { //… this.eventBus = eventBus; } public > Try dispatch(C command) { return handlers.get(command.getClass()) .map(h -> execute(h, command)) .getOrElse(() -> Try.failure( new HandlerNotFoundException(command))); } private > Try execute(
 CommandHandler h, C command) { return Try.of(() -> { Tuple2> result = h.handle(command); result._2.forEach(eventBus::publish); return result._1; }); } }

Slide 54

Slide 54 text

D O M A I N E V E N T P R O J E C T I O N

Slide 55

Slide 55 text

L E T ’ S G O B A C K I N T H E C O M M A N D H A N D L E R U S E R C O M M A N D Q U E RY Q U E RY H A N D L E R C O M M A N D H A N D L E R Q U E RY B U S C O M M A N D B U S we are here…

Slide 56

Slide 56 text

O U R C O M M A N D H A N D L E R A G G R E G AT E E V E N T S A G G R E G AT E domain operations handler(state, command) = (state’, [event]) domain event

Slide 57

Slide 57 text

A N D R E T U R N S T H E M public List handle(RelocateHubCommand command) {
 Hub hub = repository.getHub(command.hubId);
 events.add(hub.relocateTo(command.newLocationId));
 
 return events;
 }
 
 private List events = List.empty();

Slide 58

Slide 58 text

T O B E D I S PAT C H E D B Y A M I D D L E WA R E C O M M A N D C O M M A N D H A N D L E R C O M M A N D B U S VA L I D AT I O N U N I T O F W O R K E V E N T D I S PAT C H E R S E N D E M A I L L A U N C H C O M M A N D

Slide 59

Slide 59 text

W E C A N L I S T E N E V E N T T O U P D AT E O T H E R D ATA S T O R E public class HubLocationsUpdater implements EventHandler {
 
 public HubLocationsUpdater(Connection sql) {
 this.sql = sql;
 }
 
 public void handle(HubMovedEvent event) {
 sql.createStatement().executeUpdate(
 "UPDATE hub_location SET location_id = ? WHERE hub_id = ?"
 ).execute(event.newLocationId, event.hubId);
 }
 
 public Class extends Event> listenTo() {
 return HubMovedEvent.class;
 }
 }

Slide 60

Slide 60 text

W E C A N C R E AT E A R E A D M O D E L I N T H E S A M E D ATA S T O R E C O M M A N D Q U E RY Q U E RY H A N D L E R C O M M A N D H A N D L E R P R O J E C T I O N U P D AT E R E V E N T D I S PAT C H E R P E R S I S T E N C E W R I T E M O D E L R E A D M O D E L

Slide 61

Slide 61 text

O R I N D I F F E R E N T O N E C O M M A N D Q U E RY Q U E RY H A N D L E R C O M M A N D H A N D L E R P R O J E C T I O N U P D AT E R E V E N T D I S PAT C H E R W R I T E M O D E L R E A D M O D E L This is not event sourcing !

Slide 62

Slide 62 text

O P E N I N G A W O R L D O F P O S S I B L E G E O Q U E RY S E A R C H U P D AT E R D O M A I N E V E N T G I S D B G I S U P D AT E R S Q L U P D AT E R E L A S T I C S E A R C H P O S T G R E S Q L S E A R C H Q U E RY R E L AT I O N A L Q U E RY You can adapt your datastore to your queries need plus you can even add more later…

Slide 63

Slide 63 text

TA K E A WAY • Enable reactive business system • Adapt the datastore to your queries need • Suited even for legacy system

Slide 64

Slide 64 text

E V E N T S O U R C I N G AT L E A S T …

Slide 65

Slide 65 text

W E A D D S O M E M E TA D ATA T O E V E N T S public abstract class Event> {
 
 public Event(ENTITY entity) {
 aggregateId = entity.id();
 aggregateType = entity.class;
 }
 
 public ID aggregateId;
 public Class aggregateType; public LocalDateTime eventDateTime = LocalDateTime.now();
 }

Slide 66

Slide 66 text

W E A D D S O M E M E TA D ATA T O E V E N T S public class HubMovedEvent extends Event> {
 
 public HubMovedEvent(
 Hub hub,
 String oldLocationId,
 String newLocationId
 ) {
 super(hub);
 this.hubId = hub.id().toString();
 this.oldLocationId = oldLocationId;
 this.newLocationId = newLocationId; 
 } 
 public String hubId;
 public String newLocationId;
 public String oldLocationId;
 
 }

Slide 67

Slide 67 text

D O M A I N E V E N T A S S O U R C E O F S TAT E public class HubEventApplier implements EventApplier {
 
 public static Hub apply(Hub root, HubMovedEvent event) {
 Hub hub = new Hub(root);
 hub.locationId = UUID.fromString(event.newLocationId);
 
 return hub;
 }
 
 } look like a reducer, no ?

Slide 68

Slide 68 text

F R O M B U S I N E S S O P E R AT I O N A S M U TAT I O N public Event relocateTo(String newLocationId) {
 HubMovedEvent event = new HubMovedEvent(
 this,
 this.locationId.toString(),
 newLocationId
 );
 
 this.locationId = UUID.fromString(newLocationId);
 
 return event;
 }

Slide 69

Slide 69 text

T O E V E N T S O U R C E D B U S I N E S S O P E R AT I O N S public Tuple2 relocateTo(String newLocationId) {
 HubMovedEvent event = new HubMovedEvent(
 this,
 this.locationId.toString(),
 newLocationId
 );
 
 return Tuple.of(
 HubEventApplier.apply(this, event),
 event
 );
 }

Slide 70

Slide 70 text

R E A L E V E N T A P P L I E R public class HubEventApplier implements EventApplier {
 
 static Hub applyHubMovedEvent(Hub h, HubMovedEvent event) {
 Hub hub = new Hub(h);
 hub.locationId = UUID.fromString(event.newLocationId);
 return hub;
 }
 
 public static Hub apply(Hub hub, Event event) {
 return appliers .get(event.getClass()) .apply(hub, event);
 }
 
 static Map appliers = HashMap.of(
 HubMovedEvent.class, (hub, event) -> applyHubMovedEvent(hub, event)
 );
 }

Slide 71

Slide 71 text

A N D S AV E D I N E V E N T S T O R E C O M M A N D C O M M A N D H A N D L E R E V E N T S T O R E this is event sourcing E V E N T P E R S I S T E N C E C O M M A N D C O M M A N D H A N D L E R D B U N I T O F W O R K flush serialize

Slide 72

Slide 72 text

W H AT I S A N E V E N T S T O R E ?

Slide 73

Slide 73 text

W H AT I S A N E V E N T S T O R E ?

Slide 74

Slide 74 text

E V E N T S O U R C I N G R E B U I L D I N G S TAT E

Slide 75

Slide 75 text

W E G E T E V E N T S F O R A G I V E N A G G R E G AT E public class eventStore {
 public eventStore(Connection sql) {
 this.sql = sql;
 }
 
 public List get(String type, String id) {
 Seq records = sql.createStatement(
 "
 SELECT * FROM events 
 WHERE aggr_type = ? AND aggr_id = ? 
 ORDER BY TIMESTAMP "
 ).execute(aggregateType, aggregateId);
 
 return records.map(records -> Serializer.deserialize( record.getValue("payload"), Event.class
 ));
 }
 }

Slide 76

Slide 76 text

A N D W E A P P LY T H E M public HubRepository(EventStore store) {
 
 this.store = store;
 }
 
 public Hub get(UUID uuid) {
 List events = store.get("HUB", uuid.toString());
 return events.foldLeft(
 new Hub(),
 (hub, event) -> HubEventApplier.apply(hub, event)
 );
 }

Slide 77

Slide 77 text

TA K E A WAY • No more business memory loss • Pure data persistence (no more plop) • You can build new projection from past events

Slide 78

Slide 78 text

F O L D E R H I E R A R C H Y

Slide 79

Slide 79 text

THE BIG PICTURE COMMAND HANDLER BUS ACK/NACK EVENT DISPATCHER REPOSITORY PROJECTOR PROJECTOR QUERY HANDLER BUS VIEWMODEL EVENT STORE EVENTS AS A SOURCE OF TRUTH WE CAN CREATE NEW PROJECTION ON DEMAND

Slide 80

Slide 80 text

H O W W E H A N D L E C O M M A N D • classic : 
 handler(state, command) = state’ • with domain event : 
 handler(state, command = (state’, [event]) • with event sourcing :
 handler([event], command) = [event]’

Slide 81

Slide 81 text

Q & A • How do we snapshot ? • How do we manage version ? • How to use multiple aggregate in a handler ?

Slide 82

Slide 82 text

T O G O F U R T H E R • CQRS Documents by Greg Young • The Value of Value by Rich Hickey • What if the user was a function by Andre Staltz

Slide 83

Slide 83 text

@ L I L O B A S E | L G O . G R O U P Thanks !