Hardware account #123 Book 10£ for a new mouse Acc. #123 increased by 10£ Intention Outcome Actor Behavior Always! Bookkeeping system Example 1: Simple Profit and Loss Accounting @martinschimak
Book 10£ for a new mouse Hardware account #123 Acc. #123 increased by 10£ Consistency Boundary Cash account #654 Example 2: Double Entry Accounting Intention Outcome Actor Behavior Acc. #654 decreased by 10£ Provided balance > = 0 Nothing changed or Accounting Service @martinschimak
Potentially dangerous pattern Book 10£ for a new mouse Hardware account #123 Acc. #123 increased by 10£ Cash account #654 Example 3: Double Entry Accounting Intention Outcome Actor Behavior Acc. #654 decreased by 10£ Always! Provided balance > = 0 Nothing changed or Consistency Boundary Accounting Service Network Boundary
Intention Outcome Actor Book 10£ for a new mouse Hardware account #123 Consistency Boundary Cash account #654 Acc. #123 increased by 10£ Acc. #654 decreased by 10£ Eventual Consistency Async after persistence @martinschimak
Hardware account #123 Aggregate Boundary Acc. #123 increased by 10£ Persisting the (last) state When persisting the structural state an event can be a transactional “by- product” of persisting a new state! The aggregate consistency is e.g. protected by transactions and optimistic locking @martinschimak
Hardware account #123 Aggregate Boundary Acc. #123 increased by 10£ Persisting (all) the change The aggregate consistency is e.g. protected by append only logs guaranteeing unique ids With Event Sourcing events are the only persistent “source of truth”, state is dynamically derived in memory Acc. #123 increased by 10£ Acc. #123 increased by 10£ @martinschimak
Intention Outcome Actor Book 10£ for a new mouse Hardware account #123 Cash account #654 Acc. #123 increased by 10£ Acc. #654 decreased by 10£ Leo Actor Eventual Consistency @martinschimak
Intention Outcome Actor Book 10£ for a new mouse Hardware account #123 Cash account #654 Acc. #123 increase requested Acc. #654 decreased by 10£ Leo Actor Eventual Consistency Acc. #123 increased by 10£ @martinschimak
Intention Outcome Actor Book 10£ for a new mouse Hardware account #123 Cash account #654 Acc. #123 increase requested Acc. #654 decrease rejected Leo Actor Eventual Consistency Acc. #123 increase rejected @martinschimak
Intention Actor Book 10£ for a new mouse Hardware account #123 Cash account #654 Book 10£ for a new mouse Sync. within orig. request Network Boundary Consistency Boundary Example 3: Double Entry Accounting Potentially dangerous pattern
Intention Outcome Actor Book 10£ for a new mouse Hardware account #123 Consistency Boundary Cash account #654 Acc. #123 increased by 10£ Acc. #654 decreased by 10£ Eventual Consistency Immutability and Idempotency @martinschimak
@martinschimak Intention Outcome Actor Resource Knowing about change early on Order Payment Inventory Shipment Order Summary Eventual Consistency Customer Payment retrieved Goods fetched Goods shipped
Overly Central Controllers? Temporal Coupling? Sea of Events? Sharing Everything? Unidirectional Event Chains? Overly Vertical Decomposition? Central Reactors? Eventually Consistent eactions Event Driven Workflows Immutability and Idempotency Overly Central Controllers? Sea of Events? Sharing Everything? Unidirectional Event Chains? Overly Vertical Decomposition? Central Reactors? Eventually Consistent eactions Event Driven Workflows Immutability and Idempotency Knowing Change Early On Implicit Flows?
Overly Central Controllers? Sea of Events? Sharing Everything? Unidirectional Event Chains? Overly Vertical Decomposition? Central Reactors? Eventually Consistent eactions Event Driven Workflows Immutability and Idempotency Knowing Change Early On Sea of Events? Sharing Everything? Unidirectional Event Chains? Overly Vertical Decomposition? Central Reactors? Eventually Consistent eactions Event Driven Workflows Immutability and Idempotency Knowing Change Early On More Decentral Choreography Implicit Flows?
Make the context boundary explicit Amount credited { "context": "Bookkeeping" "name":"AmountCredited", "id":{ … }, … } @martinschimak Delimiting bounded contexts on moving parts ... makes sense both 1. across multiple fine-grained deployment units <= bounded context 2. within a coarse-grained monolith deployment unit > = bounded context
Sea of Events? Sharing Everything? Unidirectional Event Chains? Overly Vertical Decomposition? Central Reactors? Eventually Consistent eactions Event Driven Workflows Immutability and Idempotency Knowing Change Early On More Decentral Choreography Sharing Everything? Unidirectional Event Chains? Overly Vertical Decomposition? Central Reactors? Eventually Consistent eactions Event Driven Workflows Immutability and Idempotency Knowing Change Early On More Decentral Choreography Explicitly Bounded Contexts Implicit Flows?
Make the visibility boundary explicit @martinschimak Amount credited Internal Event Bus if internal if public Public Event Bus Head over to Mathias Verraes' blog!
For "public" events, consider to share "less" @martinschimak Account closed if public { "context": "BookKeeping" "name":"AccountClosed", "id":{ … } } Public integration events. Consider - a dedicated bounded context - explicit public event properties - coarse-grained summary events Public Event Bus Head over to Mathias Verraes' blog!
Potentially dangerous pattern Asynchronous Bus Actor "Intentions in disguise" Payment Outcome { "context": "Order" "name":"PaymentRequired", "id":{ … } } Order Payment required Language Context of Sender Billable Order generated Order placed Billable Order generated
Asynchronous Bus Actor Intention Explicit intentions Payment { "context": "Payment" "name":"RetrievePayment", "id":{ … } … } Language Context of Receiver Order Retrieve payment @martinschimak
Asynchronous Bus Actor Intention Payment Language Context of Receiver Payment retrieved Outcome Retrieve payment Payment failed Outcome may be a business success or failure Promising? Yes, some outcome! Order @martinschimak
Asynchronous Bus Actor Intention Payment Language Context of Receiver Payment retrieved Outcome Retrieve payment Payment failed API modelling a reactive promise @martinschimak
Asynchronous Bus Payment Language Context of Receiver Payment retrieved Retrieve payment Payment failed Full transparency of reactive promises o Location o Composition o Execution Time @martinschimak
@martinschimak Order placed Goods shipped Payment retrieved "Implicit, unidirectional event chain" Payment Marketing Shipment Order Actor Outcome Potentially dangerous pattern "Premium customers pay after shipment" "Check identity for new customers"
Eventual consistency Binding to intentions Order Retrieve payment Sender binds to language defined by receiver! Data Flow Direction Payment Sender Receiver Language Context of Receiver Sender decides that a payment needs to be retrieved! depends on @martinschimak
Payment Binding to outcomes Receiver binds to language defined by sender! Order Order placed Language Context of Sender Eventual consistency Data Flow Direction depends on Sender Receiver Receiver decides that a payment needs to be retrieved! @martinschimak
Balanced decomposition Vertical (functional) decomposition Horizontal (capability) decomposition may foster too much hierarchy may foster too much implicitness A balanced decomposition leverages both options as appropriate
Business Function Retrieve payment Credit card charged create on Payment Order placed on Charge credit card create Credit Card create on Payment create @martinschimak Delegation of (part of) promise Execution of promise Interpretation of observation on Consistency Boundaries Payment retrieved on Fulfillment Consequence of observation Vertical decomposition
Ship goods create on create @martinschimak Interpretation of observation Payment retrieved Fulfillment Consequence of observation Payment Promote customer on Promotions Consequence of observation create Horizontal decomposition Business Capability Consistency Boundaries
Aggregate Intention Outcome create on Aggregate Outcome on Intention create Aggregate create on Aggregate create @martinschimak Consequence of observation Delegation of (part of) promise Execution of promise Interpretation of observation on Consistency Boundaries Intention on Aggregate Consequence of observation create Asynchronous and reactive aggregates
Aggregate Intention create on Aggregate Outcome on Intention create Aggregate create @martinschimak Consequence of observation Delegation of (part of) promise Interpretation of observation Consistency Boundaries Intention on Aggregate Consequence of observation create A bit seldom discussed Outcome on Aggregates handling sagas and processes
Actor Intention Consistency Boundary create Outcome create Intention Outcome either or and/or How does look like in domain code? Behavior Behavior @martinschimak
Credit Card Consistency Boundary Credit card charged create Charge credit card Execution of promise fun handle(intention: ChargeCreditCard): Fact { // (in real life execution would still be composite, see later) return CreditCardCharged( /* Constructor params here */ ) } @martinschimak
Payment Eventually Consistent Saga or Business Process Payment retrieved create Retrieve payment Composite execution of promise How could look like in domain code? Composite behavior @martinschimak
Handlers implementing state transitions class Payment { fun handle(fact: RetrievePayment): Fact { // Business decision logic here return ChargeCreditCard( /* Constructor params here */ ) } fun handle(fact: CreditCardCharged): Fact { // Business decision logic here return PaymentRetrieved( /* Constructor params here */ ) } fun handle(fact: CreditCardFailed): Fact { // … } @martinschimak
A typed DSL declaring a saga or process flow define { on message(RetrievePayment::class) … execute service { create intention(ChargeCreditCard::class) by { … } on message(CreditCardCharged::class) having "reference" match { paymentId } … } create success(PaymentRetrieved::class) by { PaymentRetrieved(paymentId, covered) } } @martinschimak
A typed DSL declaring a saga or process flow + Clean, readable code for composite saga/process behavior + Options to execute declared state machine with different means + Automatic generation of (living, graphical) documentation + Everything needed in one place (flow, data, correlation, …) MVP coming soon! https://github.com/factdriven @martinschimak