41
Decoupling with (Domain-)Events SymfonyCon Warsaw Benjamin Eberlei (@beberlei) December 2013

Decoupling with (Domain-)Events - SymfonyCon Warsaw

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

Decoupling with (Domain-)EventsSymfonyCon Warsaw

Benjamin Eberlei (@beberlei)December 2013

Me

I Working at Qafoo

We offer trainings and consulting to help our customerscreate high quality code.http://qafoo.com

I Doctrine Developer

I Symfony Contributor

I Twitter @beberlei and @qafoo

Outline

Motivation

Example

Events

Technical Implications

Domain Events

Empirical Observation

Just using Symfony and Doctrinedoes not prevent us from creating

high coupling and legacy code.

Our Mistakes

Thinking in technical use-cases

.. leads to huge controllers

Our Mistakes

One model to rule them all

.. leads to monolithic object-graphs

Our Mistakes

No abstraction of business rules

.. prevents reusability of code

What would we prefer?

I Small controllersI Reusable business logicI Recombinable business logicI Decoupled bundles

Outline

Motivation

Example

Events

Technical Implications

Domain Events

Example: Establish Contact

I User A requests Contact to User BI Contact request triggers:

I notification mailI activity stream item

I Accept or Decline contact requestsI with notifications again

I Accepting contact notifies other systems, example:I Synchronization of dataI IndexingI PaymentI Metrics/Logging

Example: Establish Contact

Example: Establish Contact

Demo

Questions

I Does creating services solve the problem?I How many dependencies has the controller?I To which bundles do the controller dependencies link?I To which bundles do the entity dependencies link?I How would you enable/disable/recombine features?

Fixing Service Dependencies

I Apply Dependency Inversion (SOLID principles)I Use interfaces for operations/servicesI Bundle that uses operation provides interfaceI Other bundle implements that interface

Example: Establish Contact

Fixing Entity Dependencies: Bounded Contexts

A BOUNDED CONTEXT delimits the applicability of aparticular model so that team members have a clear andshared understanding of what has to be consistent andhow it relates to otherCONTEXTS. (Evans in Domain-Driven Design)

Example: Establish Contact

Problems

That still leads us with

I a hardcoded process/workflowI too many dependencies in the controllersI cognitive overload, everything is important

Outline

Motivation

Example

Events

Technical Implications

Domain Events

What are Events?

An event can be defined as asignificant change instate (Wikipedia)

Using Events

I State change makes the event happenI Application creates Message Object for the eventI Message Object is passed to a dispatcher/publisherI Listeners are notified of the event

Allows decoupling compontents even more!

Outline

Motivation

Example

Events

Technical Implications

Domain Events

Properties of Event Messages

I No return value?I No way to stop execution/propagatoin?I Failure does not affect the event emitter?I Asynchronous?I Serializable?

Complexity through Event-based Architecture

I ACID transactionsI Requires two-phase commit between datastorage and event

dispatcherI Leads to complicated transaction management

I BASE transactionsI Eventually Consistent data storageI Enables ”pull task”-based systemsI Developers responsibility to handle failures

Outline

Motivation

Example

Events

Technical Implications

Domain Events

Discussion between Developers

Developer A: Where do you handle the case to send anemail for a newly established contact between users?

Developer B: In theContactRequestCreatedPrePersistListener whenthe prePersist Event happens.

Discussion between Developers

Developer A: How do you differentiate between a contactrequest triggerd by the user and batch import?

Developer B: By using a kernel.request listener thatsets a boolean flag on theContactRequestCreatedPrePersistListener forbeing inside a web-request. Only if the flag is true theemail is sent.

A note on Symfony and Doctrine Events

Symfony and Doctrine events solve problems in theframework and database persistence contexts.

Hiding your business rules in them will cause pain.

Domain Events

”Something happend that domain experts care about”

I Events for state changes in the modelI in terms of the businessI tied to the execution of use-casesI explicit in code

Event Storming

Example: Establish Contact

I ContactRequested

I ContactEstablished

I ContactConfirmed

I ContactDeclined

I ContactImported

Approach 1: Procedural

Approach 1: Procedural

1 <?php2 class Con tac tCon t ro l l e r3 {

4 public function reques tAc t ion ( Request $request )5 {

6 / / . . .7 $eventDispatcher = $ th is −>get ( ’ even t d i spa tcher ’ ) ;8 $eventDispatcher−>pub l i sh (9 ’ con tac t . requested ’ ,

10 new ContactRequestedEvent ( $contactRequest )11 ) ;12 / / . . .13 }

14 }

Approach 1: Procedural

Benefits:I Loose couplingI Works with getters/setters and formsI Can be easily added to existing (CRUD-)application

Drawbacks:I Requires access to EventDispatcher in many placesI No two-phase commit between DB+EventsI Easy to forget triggering same event in other

actions/commands

Approach 2: Events as state

Approach 2: Events as state

1 <?php2

3 class ContactRequest4 {

5 private $events = ar ray ( ) ;6

7 public function construct ( $fromUser , $toUser )8 {

9 $ th is −>fromUser = $fromUser ;10 $ th is −>toUser = $toUser ;11

12 $ th is −>events [ ] = new ContactRequestedEvent ( $ t h i s ) ;13 }

14 }

Approach 2: Events as state

1 <?php2

3 class ContactRequest4 {

5 private $events = ar ray ( ) ;6

7 public function conf i rm ( )8 {

9 $ th is −>conf i rmed = true ;10

11 $ th is −>events [ ] = new ContactConfirmedEvent ( $ t h i s ) ;12 }

13 }

Approach 2: Events as state

Benefits:I Loose couplingI Events are always created when state changesI No dispatcher required in the model codeI Dispatching of events IFF storage tx successfulI Enables Event Sourcing

Drawbacks:I Requires deep integration into DoctrineI Does not work well with forms (unless..)

Conclusion

Decoupling can be achieved with

I interfaces for dependency inversionI cutting the assocations between entitiesI using events to communicate between bundles

Further Readings

I http://www.whitewashing.de/2013/07/24/doctrine and domainevents.html

I http://www.whitewashing.de/2013/06/24/bounded contexts.html

I http://www.whitewashing.de/2013/06/27/extending symfony2 controller utilities.html

I http://verraes.net/2013/04/decoupling-symfony2-forms-from-entities/

https://joind.in/talk/view/10369