SlideShare una empresa de Scribd logo
1 de 77
Exploring Lightweight
   Event Sourcing
Erik Rozendaal <erozendaal@zilverline.com>
              @erikrozendaal
Goals

• The problem of data persistence
• Understand event sourcing
• Show why Scala is well-suited as
  implementation language



              Exploring lightweight event sourcing   2
Problem



Exploring lightweight event sourcing   3
N-tier




Exploring lightweight event sourcing   4
N-tier
• Presentation, Business, and Data Layers




              Exploring lightweight event sourcing   4
N-tier
• Presentation, Business, and Data Layers
• Shared data model (“domain”)




              Exploring lightweight event sourcing   4
N-tier
• Presentation, Business, and Data Layers
• Shared data model (“domain”)
• Heavy use of a single, global, mutable
  variable (“the database”)




              Exploring lightweight event sourcing   4
N-tier
• Presentation, Business, and Data Layers
• Shared data model (“domain”)
• Heavy use of a single, global, mutable
  variable (“the database”)
• Is it any surprise that we’re struggling to
  build modular, maintainable applications?


               Exploring lightweight event sourcing   4
Domain Driven Design
•   Domain-Driven Design An approach to software development that suggests
    that (1) For most software projects, the primary focus should be on the
    domain and domain logic; and (2) Complex domain designs should be based
    on a model.

•   Domain Expert A member of a software project whose field is the domain
    of the application, rather than software development. Not just any user of
    the software, the domain expert has deep knowledge of the subject.

•   Ubiquitous Language A language structured around the domain model and
    used by all team members to connect all the activities of the team with the
    software.




                         Exploring lightweight event sourcing                     5
Data must be durable




      Exploring lightweight event sourcing   6
But current
applications are lossy

       Exploring lightweight event sourcing   7
Lossy?




Exploring lightweight event sourcing   8
Lossy?
• UPDATE invoice WHERE id = 1234
    SET total_amount = 230




            Exploring lightweight event sourcing   8
Lossy?
• UPDATE invoice WHERE id = 1234
    SET total_amount = 230
 • What happened to the previous order
   amount?




             Exploring lightweight event sourcing   8
Lossy?
• UPDATE invoice WHERE id = 1234
    SET total_amount = 230
 • What happened to the previous order
   amount?
 • Why was the order amount changed?

             Exploring lightweight event sourcing   8
Lossy?
• UPDATE invoice WHERE id = 1234
     SET total_amount = 230
  • What happened to the previous order
    amount?
  • Why was the order amount changed?
• Application behavior is not captured!
              Exploring lightweight event sourcing   8
Behavior?




Exploring lightweight event sourcing   9
Behavior?

Status:
"Draft"


          Total:
Invoice
          0.00




                   Exploring lightweight event sourcing   9
Behavior?

Status:                  Status:          Recipient:
"Draft"                  "Draft"            "Erik"


          Total:                            Total:
Invoice                  Invoice
          0.00                              0.00




                   Exploring lightweight event sourcing   9
Behavior?

Status:                  Status:          Recipient:        Status:      Recipient:
"Draft"                  "Draft"            "Erik"          "Draft"        "Erik"


          Total:                            Total:                         Total:
Invoice                  Invoice                            Invoice
          0.00                              0.00                           9.95


                                                                           Item:
                                                          Invoice Item
                                                                          "Food"


                                                                         Amount:
                                                                          9.95




                   Exploring lightweight event sourcing                               9
It’s just data mutation

Status:                  Status:          Recipient:        Status:      Recipient:
"Draft"                  "Draft"            "Erik"          "Draft"        "Erik"


          Total:                            Total:                         Total:
Invoice                  Invoice                            Invoice
          0.00                              0.00                           9.95


                                                                           Item:
                                                          Invoice Item
                                                                          "Food"


                                                                         Amount:
                                                                          9.95




                   Exploring lightweight event sourcing                               10
It’s just data mutation

             Status:          Recipient:
             "Draft"            "Erik"


                                Total:
             Invoice
                                9.95


                                Item:
           Invoice Item
                               "Food"


                              Amount:
                               9.95




       Exploring lightweight event sourcing   10
ORM implementation

“Inexperienced programmers love magic because
it saves their time. Experienced programmers hate
magic because it wastes their time.”
– @natpryce




                Exploring lightweight event sourcing   11
Event Sourcing

• All state changes are explicitly captured
  using domain events
• Capture the intent of the user and the
  related data
• Events represent the outcome of application
  behavior


                 Exploring lightweight event sourcing   12
Domain Behavior




    Exploring lightweight event sourcing   13
Domain Behavior
   generate


Draft Invoice
  Created




                    Exploring lightweight event sourcing   13
Domain Behavior
   generate


Draft Invoice
  Created




                apply

                        Status:
                        "Draft"


                                  Total:
                        Invoice
                                  0.00




                                       Exploring lightweight event sourcing   13
Domain Behavior
   generate


Draft Invoice                         Invoice Recipient
  Created                                 Changed
                                      Recipient: "Erik"




                apply             generate

                        Status:
                        "Draft"


                                         Total:
                        Invoice
                                         0.00




                                              Exploring lightweight event sourcing   13
Domain Behavior
   generate


Draft Invoice                         Invoice Recipient
  Created                                 Changed
                                      Recipient: "Erik"




                apply             generate                apply

                        Status:                                   Status:     Recipient:
                        "Draft"                                   "Draft"       "Erik"


                                         Total:                                 Total:
                        Invoice                                   Invoice
                                         0.00                                   0.00




                                              Exploring lightweight event sourcing         13
Domain Behavior
   generate


Draft Invoice                         Invoice Recipient                           Invoice Item
  Created                                 Changed                                    Added
                                      Recipient: "Erik"                         Item: "Food"
                                                                                Item amount: 9.95
                                                                                Total amount: 9.95



                apply             generate                apply             generate

                        Status:                                   Status:       Recipient:
                        "Draft"                                   "Draft"         "Erik"


                                         Total:                                   Total:
                        Invoice                                   Invoice
                                         0.00                                     0.00




                                              Exploring lightweight event sourcing                   13
Domain Behavior
   generate


Draft Invoice                         Invoice Recipient                           Invoice Item
  Created                                 Changed                                    Added
                                      Recipient: "Erik"                         Item: "Food"
                                                                                Item amount: 9.95
                                                                                Total amount: 9.95



                apply             generate                apply             generate             apply

                        Status:                                   Status:       Recipient:               Status:    Recipient:
                        "Draft"                                   "Draft"         "Erik"                 "Draft"      "Erik"


                                         Total:                                   Total:                              Total:
                        Invoice                                   Invoice                                Invoice
                                         0.00                                     0.00                                9.95


                                                                                                                      Item:
                                                                                                     Invoice Item
                                                                                                                     "Food"


                                                                                                                    Amount:
                                                                                                                     9.95




                                              Exploring lightweight event sourcing                                               13
Only the events need
to be stored on disk

    Event
    Store    Draft Invoice   Invoice Recipient     Invoice Item

                                                                      .......
               Created           Changed              Added
                             Recipient: "Erik"   Item: "Food"
                                                 Item amount: 9.95
                                                 Total amount: 9.95




        Exploring lightweight event sourcing                                    14
Reloading from history




       Exploring lightweight event sourcing   15
Reloading from history
Draft Invoice
  Created




                apply

                        Status:
                        "Draft"


                                  Total:
                        Invoice
                                  0.00




                                       Exploring lightweight event sourcing   15
Reloading from history
Draft Invoice                     Invoice Recipient
  Created                             Changed
                                  Recipient: "Erik"




                apply                                 apply

                        Status:                               Status:     Recipient:
                        "Draft"                               "Draft"       "Erik"


                                     Total:                                 Total:
                        Invoice                               Invoice
                                     0.00                                   0.00




                                          Exploring lightweight event sourcing         15
Reloading from history
Draft Invoice                     Invoice Recipient                         Invoice Item
  Created                             Changed                                  Added
                                  Recipient: "Erik"                       Item: "Food"
                                                                          Item amount: 9.95
                                                                          Total amount: 9.95



                apply                                 apply                                apply

                        Status:                               Status:     Recipient:               Status:    Recipient:
                        "Draft"                               "Draft"       "Erik"                 "Draft"      "Erik"


                                     Total:                                 Total:                              Total:
                        Invoice                               Invoice                              Invoice
                                     0.00                                   0.00                                9.95


                                                                                                                Item:
                                                                                               Invoice Item
                                                                                                               "Food"


                                                                                                              Amount:
                                                                                                               9.95




                                          Exploring lightweight event sourcing                                             15
Reloading from history
Draft Invoice                     Invoice Recipient                         Invoice Item
  Created                             Changed                                  Added
                                  Recipient: "Erik"                       Item: "Food"
                                                                          Item amount: 9.95
                                                                          Total amount: 9.95



                apply                                 apply                                apply

                        Status:                               Status:     Recipient:               Status:    Recipient:
                        "Draft"                               "Draft"       "Erik"                 "Draft"      "Erik"


                                     Total:                                 Total:                              Total:
                        Invoice                               Invoice                              Invoice
                                     0.00                                   0.00                                9.95


                                                                                                                Item:
                                                                                               Invoice Item
                                                                                                               "Food"


                                                                                                              Amount:
                                                                                                               9.95




                                          Exploring lightweight event sourcing                                             15
YAGNI?




Exploring lightweight event sourcing   16
YAGNI?
• On the checkout page we’d like to promote
  products that customers previously
  removed from their shopping cart.




             Exploring lightweight event sourcing   16
YAGNI?
• On the checkout page we’d like to promote
  products that customers previously
  removed from their shopping cart.
• Can you tell us how many people removed
  a product but bought it later anyway?




              Exploring lightweight event sourcing   16
YAGNI?
• On the checkout page we’d like to promote
  products that customers previously
  removed from their shopping cart.
• Can you tell us how many people removed
  a product but bought it later anyway?
• ... over the past 5 years?
  ... and how much time passed in-between?


               Exploring lightweight event sourcing   16
YAGNI?

• We have reports of a strange bug, but have
  not been able to isolate it. Could you look
  at the production data to find out what’s
  going on?




              Exploring lightweight event sourcing   17
Is it worth it?




   Exploring lightweight event sourcing   18
Is it worth it?

• Make the event sourcing implementation as
  simple as possible




              Exploring lightweight event sourcing   18
Is it worth it?

• Make the event sourcing implementation as
  simple as possible
• ... while avoiding the complexities of
  databases, ORMs, etc.




               Exploring lightweight event sourcing   18
Is it worth it?

• Make the event sourcing implementation as
  simple as possible
• ... while avoiding the complexities of
  databases, ORMs, etc.
  • ... unless you want or need them :)

               Exploring lightweight event sourcing   18
Ingredients




 Exploring lightweight event sourcing   19
Ingredients
•   Scala case classes




                     Exploring lightweight event sourcing   19
Ingredients
•   Scala case classes

•   Immutable data
    structures




                     Exploring lightweight event sourcing   19
Ingredients
•   Scala case classes

•   Immutable data
    structures

•   Single Responsibility
    Principle (SRP)




                     Exploring lightweight event sourcing   19
Ingredients
•   Scala case classes

•   Immutable data
    structures

•   Single Responsibility
    Principle (SRP)

•   Events for durability




                     Exploring lightweight event sourcing   19
Ingredients
•   Scala case classes

•   Immutable data
    structures

•   Single Responsibility
    Principle (SRP)

•   Events for durability

•   Memory Image


                     Exploring lightweight event sourcing   19
“CRUD”




Exploring lightweight event sourcing   20
“CRUD”
Create/Update/Delete




                       Exploring lightweight event sourcing   20
“CRUD”
Create/Update/Delete


                          HTTP POST
                                            Validate input and
                                             generate event




                       Exploring lightweight event sourcing      20
“CRUD”
Create/Update/Delete


                          HTTP POST                              Save Event
                                            Validate input and                Event
                                             generate event                   Store




                       Exploring lightweight event sourcing                           20
“CRUD”
Create/Update/Delete


                          HTTP POST                              Save Event
                                            Validate input and                                 Event
                                             generate event                                    Store



                                                                              Apply Event




                                                                                  Status:              Recipient:
                                                                                  "Draft"                "Erik"




                                                                                  Invoice              Total: 9.95




                                                                                                         Item:
                                                                                Invoice Item
                                                                                                        "Food"



                                                                                                        Amount:
                                                                                                         9.95




                       Exploring lightweight event sourcing                                                          20
“CRUD”
Create/Update/Delete


                          HTTP POST                              Save Event
                                            Validate input and                                 Event
                                             generate event                                    Store



                                                                              Apply Event




                                                                                  Status:              Recipient:
                                                                                  "Draft"                "Erik"




                                                                                  Invoice              Total: 9.95
                                                                 Query
                                              Render View
                                                                                                         Item:
                                                                                Invoice Item
                                                                                                        "Food"



                                                                                                        Amount:
                                                                                                         9.95




                       Exploring lightweight event sourcing                                                          20
“CRUD”
Create/Update/Delete


                          HTTP POST                              Save Event
                                            Validate input and                                 Event
                                             generate event                                    Store



                                                                              Apply Event




       Read                                                                       Status:
                                                                                  "Draft"
                                                                                                       Recipient:
                                                                                                         "Erik"




                                                                                  Invoice              Total: 9.95
                         HTTP GET                                Query
                                              Render View
                                                                                                         Item:
                                                                                Invoice Item
                                                                                                        "Food"



                                                                                                        Amount:
                                                                                                         9.95




                       Exploring lightweight event sourcing                                                          20
HTTP POST                        Save Event
                                                                          Validate input and                                 Event




Controller
                                                                           generate event                                    Store



                                                                                                            Apply Event




                                                                                                                Status:              Recipient:
                                                                                                                "Draft"                "Erik"




                                                                                                                Invoice              Total: 9.95
                                                              HTTP GET                         Query
                                                                            Render View
                                                                                                                                       Item:
                                                                                                              Invoice Item
                                                                                                                                      "Food"



                                                                                                                                      Amount:
                                                                                                                                       9.95




post("/todo") {
  val item = field("item", required) map (_.collapseWhitespace)
  item.fold(
    error => new ToDoView(toDoItems, Some(error)),
    value => {
      record(Created(ToDoItem(UUID.randomUUID(), value)))
      redirect(url("/todo"))
    })
}




                       Exploring lightweight event sourcing                                                                                        21
HTTP POST                        Save Event
                                                                              Validate input and                                 Event




Memory Image
                                                                               generate event                                    Store



                                                                                                                Apply Event




                                                                                                                    Status:              Recipient:
                                                                                                                    "Draft"                "Erik"




                                                                                                                    Invoice              Total: 9.95
                                                                  HTTP GET                         Query
                                                                                Render View
                                                                                                                                           Item:
                                                                                                                  Invoice Item
                                                                                                                                          "Food"



                                                                                                                                          Amount:
                                                                                                                                           9.95




case class ToDoItems (
    all          : Map[String, ToDoItem] = Map.empty,
    recentlyAdded: Vector[String]        = Vector.empty) {

    def apply(event: Any) = event match {
      case Created(item: ToDoItem) =>
        copy(all.updated(item.id, item), recentlyAdded :+ item.id)
      case Updated(item: ToDoItem) =>
        copy(all.updated(item.id, item))
      case Deleted(id: String) =>
        copy(all - id, recentlyAdded.filter(_ != id))
    }

    def mostRecent(count: Int) = recentlyAdded.takeRight(count).map(all).reverse
}




                           Exploring lightweight event sourcing                                                                                        22
HTTP POST                        Save Event
                                                                            Validate input and                                 Event




View
                                                                             generate event                                    Store



                                                                                                              Apply Event




                                                                                                                  Status:              Recipient:
                                                                                                                  "Draft"                "Erik"




                                                                                                                  Invoice              Total: 9.95
                                                                HTTP GET                         Query
                                                                              Render View
                                                                                                                                         Item:
                                                                                                                Invoice Item
                                                                                                                                        "Food"



                                                                                                                                        Amount:
                                                                                                                                         9.95




<table class="zebra-striped">
  <thead><tr><th>To-Do</th></tr></thead>
  <tbody>{
    for (item <- toDoItems.mostRecent(20)) yield {
      <tr><td>{ item.text }</td></tr>
    }
  }</tbody>
</table>




                         Exploring lightweight event sourcing                                                                                        23
Domain Model
                                         Domain Model


                                                                               2: Save Event

                      1: Send Command
 Validate input and
generate command                                                                                           Event Store

                                                                               3: Apply Event



                                                                                                3: Apply Event




                                                                                                     Status:         Recipient:
                                                                                                     "Draft"           "Erik"




                                                                                                     Invoice         Total: 9.95




                                                                                                                       Item:
                                                                                                   Invoice Item
                                                                                                                      "Food"



                                                                                                                      Amount:
                                                                                                                       9.95

                                                                                                Presentation Model




                                        Exploring lightweight event sourcing                                                       24
Domain Code
sealed trait InvoiceEvent
case class InvoiceCreated() extends InvoiceEvent
case class InvoiceRecipientChanged(recipient: String) extends InvoiceEvent
case class InvoiceItemAdded(item: InvoiceItem, totalAmount: BigDecimal) extends InvoiceEvent
case class InvoiceSent(sentOn: LocalDate, paymentDueOn: LocalDate) extends InvoiceEvent




                              Exploring lightweight event sourcing                             25
Domain Code
sealed trait InvoiceEvent
case class InvoiceCreated() extends InvoiceEvent
case class InvoiceRecipientChanged(recipient: String) extends InvoiceEvent
case class InvoiceItemAdded(item: InvoiceItem, totalAmount: BigDecimal) extends InvoiceEvent
case class InvoiceSent(sentOn: LocalDate, paymentDueOn: LocalDate) extends InvoiceEvent


sealed trait Invoice extends AggregateRoot {
  protected[this] type Event = InvoiceEvent
}




                              Exploring lightweight event sourcing                             25
Domain Code
case class DraftInvoice(
  recipient: Option[String] = None,
  nextItemId: Int = 1,
  items: Map[Int, InvoiceItem] = Map.empty) extends Invoice {

    def changeRecipient(recipient: String): Behavior[DraftInvoice] = ...

    def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] = ...

    def send(sentOn: LocalDate): Behavior[Either[String, SentInvoice]] = ...

    ...

    private   def recipientChanged = when[InvoiceRecipientChanged] {
      event   => copy(recipient = Some(event.recipient))
    }
    private   def itemAdded = when[InvoiceItemAdded] {
      event   => copy(nextItemId = event.item.id + 1, items = items + (event.item.id -> event.item))
    }
    private   def sent = when[InvoiceSent] {
      event   => SentInvoice(event.sentOn, event.paymentDueOn)
    }

}




                                    Exploring lightweight event sourcing                               26
Domain Code
case class DraftInvoice(
  recipient: Option[String] = None,
  nextItemId: Int = 1,                                                                The “Brains”
  items: Map[Int, InvoiceItem] = Map.empty) extends Invoice {

    def changeRecipient(recipient: String): Behavior[DraftInvoice] = ...

    def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] = ...

    def send(sentOn: LocalDate): Behavior[Either[String, SentInvoice]] = ...

    ...

    private   def recipientChanged = when[InvoiceRecipientChanged] {
      event   => copy(recipient = Some(event.recipient))
    }
    private   def itemAdded = when[InvoiceItemAdded] {
      event   => copy(nextItemId = event.item.id + 1, items = items + (event.item.id -> event.item))
    }
    private   def sent = when[InvoiceSent] {
      event   => SentInvoice(event.sentOn, event.paymentDueOn)
    }

}




                                    Exploring lightweight event sourcing                               26
Domain Code
case class DraftInvoice(
  recipient: Option[String] = None,
  nextItemId: Int = 1,                                                                The “Brains”
  items: Map[Int, InvoiceItem] = Map.empty) extends Invoice {

    def changeRecipient(recipient: String): Behavior[DraftInvoice] = ...

    def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] = ...

    def send(sentOn: LocalDate): Behavior[Either[String, SentInvoice]] = ...

    ...                                                                              The “Muscle”
    private   def recipientChanged = when[InvoiceRecipientChanged] {
      event   => copy(recipient = Some(event.recipient))
    }
    private   def itemAdded = when[InvoiceItemAdded] {
      event   => copy(nextItemId = event.item.id + 1, items = items + (event.item.id -> event.item))
    }
    private   def sent = when[InvoiceSent] {
      event   => SentInvoice(event.sentOn, event.paymentDueOn)
    }

}




                                    Exploring lightweight event sourcing                               26
Domain Code
case class DraftInvoice(
  recipient: Option[String] = None,
  nextItemId: Int = 1,
  items: Map[Int, InvoiceItem] = Map.empty) extends Invoice {

    def changeRecipient(recipient: String): Behavior[DraftInvoice] =
      recipientChanged(InvoiceRecipientChanged(recipient))

    def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] =
      itemAdded(InvoiceItemAdded(InvoiceItem(nextItemId, description, amount), totalAmount + amount))

    def send(sentOn: LocalDate): Behavior[Either[String, SentInvoice]] =
      if (readyToSend_?)
        for (updated <- sent(InvoiceSent(sentOn, paymentDueOn = sentOn.plusDays(14))))
        yield Right(updated)
      else
        pure(Left("invoice not ready to send"))

    private def totalAmount = items.values.map(_.amount).sum

    private def readyToSend_? = recipient.isDefined && items.nonEmpty

    ...
}




                                  Exploring lightweight event sourcing                                  27
Domain Code
case class DraftInvoice(
  recipient: Option[String] = None,
  nextItemId: Int = 1,
  items: Map[Int, InvoiceItem] = Map.empty) extends Invoice {

    ...

    protected[this] def applyEvent = recipientChanged orElse itemAdded orElse sent

    ...
}




                                  Exploring lightweight event sourcing               28
Event Store

• Stores sequence of events
• Dispatches events when successfully
  committed
• Replays events on startup
• It’s your application’s transaction log

               Exploring lightweight event sourcing   29
Conclusion
• Event Sourcing captures full historical
  information
• Fully encapsulated domain code decoupled
  from persistence and presentation layers
• Easier to fully understand compared to
  traditional ORM based approach
• Need to learn “Event-Driven” thinking
                Exploring lightweight event sourcing   30
Example Application

• https://github.com/erikrozendaal/scala-
  event-sourcing-example
• (soon) https://github.com/zilverline/lessdb-
  example




               Exploring lightweight event sourcing   31
Implementation
            Limitations
•   Single-threaded event store using disruptor
    pattern

    •   ~ 5.000 commits per second

•   Number of events to replay on startup

    •   ~ 70.000 JSON serialized events per second

•   Total number of objects to store in main memory

    •   JVM can run with large heaps (tens of gigabytes)


                   Exploring lightweight event sourcing    32
But ready to scale
•   Advanced event store implementations support
    SQL, MongoDB, Amazon SimpleDB, etc.

•   Use persisted view model for high volume objects
    (fixes startup time and memory usage)

•   Easy partitioning of aggregates (consistency
    boundary with unique identifier)

•   Load aggregates on-demand, use snapshotting



                  Exploring lightweight event sourcing   33
Thanks!
References
•   Example code https://github.com/erikrozendaal/scala-event-sourcing-example

•   CQRS http://cqrsinfo.com/

•   Greg Young, “Unshackle Your Domain”
    http://www.infoq.com/presentations/greg-young-unshackle-qcon08

•   Pat Helland, “Life Beyond Distributed Transactions: An Apostate's Opinion”
    http://www.ics.uci.edu/~cs223/papers/cidr07p15.pdf

•   Erik Rozendaal, “Towards an immutable domain model” http://
    blog.zilverline.com/2011/02/10/towards-an-immutable-domain-model-
    monads-part-5/

•   Martin Fowler, “Memory Image”, http://martinfowler.com/bliki/
    MemoryImage.html

•   Martin Fowler, “The LMAX Architecture”, http://martinfowler.com/articles/
    lmax.html



                         Exploring lightweight event sourcing                    35

Más contenido relacionado

Destacado

Qualities of a Good Event Planner
Qualities of a Good Event PlannerQualities of a Good Event Planner
Qualities of a Good Event Plannersandragreen876
 
Event planner - Interviewing questions
Event planner - Interviewing questions Event planner - Interviewing questions
Event planner - Interviewing questions Jen Vuhuong
 
Seminar Complex Problem Solving 2017
Seminar Complex Problem Solving 2017Seminar Complex Problem Solving 2017
Seminar Complex Problem Solving 2017Jaap Trouw
 
Corporate Event Management (general)
Corporate Event Management (general)Corporate Event Management (general)
Corporate Event Management (general)J.L. Nawan
 
Event organizing and management
Event organizing and managementEvent organizing and management
Event organizing and managementjenncalicdan0689
 
Events Management 101: For Beginners
Events Management 101: For Beginners Events Management 101: For Beginners
Events Management 101: For Beginners Orly Ballesteros
 
Event Management
Event Management Event Management
Event Management Joey Phuah
 
Developing event-driven microservices with event sourcing and CQRS (svcc, sv...
Developing event-driven microservices with event sourcing and CQRS  (svcc, sv...Developing event-driven microservices with event sourcing and CQRS  (svcc, sv...
Developing event-driven microservices with event sourcing and CQRS (svcc, sv...Chris Richardson
 

Destacado (8)

Qualities of a Good Event Planner
Qualities of a Good Event PlannerQualities of a Good Event Planner
Qualities of a Good Event Planner
 
Event planner - Interviewing questions
Event planner - Interviewing questions Event planner - Interviewing questions
Event planner - Interviewing questions
 
Seminar Complex Problem Solving 2017
Seminar Complex Problem Solving 2017Seminar Complex Problem Solving 2017
Seminar Complex Problem Solving 2017
 
Corporate Event Management (general)
Corporate Event Management (general)Corporate Event Management (general)
Corporate Event Management (general)
 
Event organizing and management
Event organizing and managementEvent organizing and management
Event organizing and management
 
Events Management 101: For Beginners
Events Management 101: For Beginners Events Management 101: For Beginners
Events Management 101: For Beginners
 
Event Management
Event Management Event Management
Event Management
 
Developing event-driven microservices with event sourcing and CQRS (svcc, sv...
Developing event-driven microservices with event sourcing and CQRS  (svcc, sv...Developing event-driven microservices with event sourcing and CQRS  (svcc, sv...
Developing event-driven microservices with event sourcing and CQRS (svcc, sv...
 

Último

Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 

Último (20)

Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 

Exploring Lightweight Event Sourcing - DUSE 2011-10-05

  • 1. Exploring Lightweight Event Sourcing Erik Rozendaal <erozendaal@zilverline.com> @erikrozendaal
  • 2. Goals • The problem of data persistence • Understand event sourcing • Show why Scala is well-suited as implementation language Exploring lightweight event sourcing 2
  • 5. N-tier • Presentation, Business, and Data Layers Exploring lightweight event sourcing 4
  • 6. N-tier • Presentation, Business, and Data Layers • Shared data model (“domain”) Exploring lightweight event sourcing 4
  • 7. N-tier • Presentation, Business, and Data Layers • Shared data model (“domain”) • Heavy use of a single, global, mutable variable (“the database”) Exploring lightweight event sourcing 4
  • 8. N-tier • Presentation, Business, and Data Layers • Shared data model (“domain”) • Heavy use of a single, global, mutable variable (“the database”) • Is it any surprise that we’re struggling to build modular, maintainable applications? Exploring lightweight event sourcing 4
  • 9. Domain Driven Design • Domain-Driven Design An approach to software development that suggests that (1) For most software projects, the primary focus should be on the domain and domain logic; and (2) Complex domain designs should be based on a model. • Domain Expert A member of a software project whose field is the domain of the application, rather than software development. Not just any user of the software, the domain expert has deep knowledge of the subject. • Ubiquitous Language A language structured around the domain model and used by all team members to connect all the activities of the team with the software. Exploring lightweight event sourcing 5
  • 10. Data must be durable Exploring lightweight event sourcing 6
  • 11. But current applications are lossy Exploring lightweight event sourcing 7
  • 13. Lossy? • UPDATE invoice WHERE id = 1234 SET total_amount = 230 Exploring lightweight event sourcing 8
  • 14. Lossy? • UPDATE invoice WHERE id = 1234 SET total_amount = 230 • What happened to the previous order amount? Exploring lightweight event sourcing 8
  • 15. Lossy? • UPDATE invoice WHERE id = 1234 SET total_amount = 230 • What happened to the previous order amount? • Why was the order amount changed? Exploring lightweight event sourcing 8
  • 16. Lossy? • UPDATE invoice WHERE id = 1234 SET total_amount = 230 • What happened to the previous order amount? • Why was the order amount changed? • Application behavior is not captured! Exploring lightweight event sourcing 8
  • 18. Behavior? Status: "Draft" Total: Invoice 0.00 Exploring lightweight event sourcing 9
  • 19. Behavior? Status: Status: Recipient: "Draft" "Draft" "Erik" Total: Total: Invoice Invoice 0.00 0.00 Exploring lightweight event sourcing 9
  • 20. Behavior? Status: Status: Recipient: Status: Recipient: "Draft" "Draft" "Erik" "Draft" "Erik" Total: Total: Total: Invoice Invoice Invoice 0.00 0.00 9.95 Item: Invoice Item "Food" Amount: 9.95 Exploring lightweight event sourcing 9
  • 21. It’s just data mutation Status: Status: Recipient: Status: Recipient: "Draft" "Draft" "Erik" "Draft" "Erik" Total: Total: Total: Invoice Invoice Invoice 0.00 0.00 9.95 Item: Invoice Item "Food" Amount: 9.95 Exploring lightweight event sourcing 10
  • 22. It’s just data mutation Status: Recipient: "Draft" "Erik" Total: Invoice 9.95 Item: Invoice Item "Food" Amount: 9.95 Exploring lightweight event sourcing 10
  • 23. ORM implementation “Inexperienced programmers love magic because it saves their time. Experienced programmers hate magic because it wastes their time.” – @natpryce Exploring lightweight event sourcing 11
  • 24. Event Sourcing • All state changes are explicitly captured using domain events • Capture the intent of the user and the related data • Events represent the outcome of application behavior Exploring lightweight event sourcing 12
  • 25. Domain Behavior Exploring lightweight event sourcing 13
  • 26. Domain Behavior generate Draft Invoice Created Exploring lightweight event sourcing 13
  • 27. Domain Behavior generate Draft Invoice Created apply Status: "Draft" Total: Invoice 0.00 Exploring lightweight event sourcing 13
  • 28. Domain Behavior generate Draft Invoice Invoice Recipient Created Changed Recipient: "Erik" apply generate Status: "Draft" Total: Invoice 0.00 Exploring lightweight event sourcing 13
  • 29. Domain Behavior generate Draft Invoice Invoice Recipient Created Changed Recipient: "Erik" apply generate apply Status: Status: Recipient: "Draft" "Draft" "Erik" Total: Total: Invoice Invoice 0.00 0.00 Exploring lightweight event sourcing 13
  • 30. Domain Behavior generate Draft Invoice Invoice Recipient Invoice Item Created Changed Added Recipient: "Erik" Item: "Food" Item amount: 9.95 Total amount: 9.95 apply generate apply generate Status: Status: Recipient: "Draft" "Draft" "Erik" Total: Total: Invoice Invoice 0.00 0.00 Exploring lightweight event sourcing 13
  • 31. Domain Behavior generate Draft Invoice Invoice Recipient Invoice Item Created Changed Added Recipient: "Erik" Item: "Food" Item amount: 9.95 Total amount: 9.95 apply generate apply generate apply Status: Status: Recipient: Status: Recipient: "Draft" "Draft" "Erik" "Draft" "Erik" Total: Total: Total: Invoice Invoice Invoice 0.00 0.00 9.95 Item: Invoice Item "Food" Amount: 9.95 Exploring lightweight event sourcing 13
  • 32. Only the events need to be stored on disk Event Store Draft Invoice Invoice Recipient Invoice Item ....... Created Changed Added Recipient: "Erik" Item: "Food" Item amount: 9.95 Total amount: 9.95 Exploring lightweight event sourcing 14
  • 33. Reloading from history Exploring lightweight event sourcing 15
  • 34. Reloading from history Draft Invoice Created apply Status: "Draft" Total: Invoice 0.00 Exploring lightweight event sourcing 15
  • 35. Reloading from history Draft Invoice Invoice Recipient Created Changed Recipient: "Erik" apply apply Status: Status: Recipient: "Draft" "Draft" "Erik" Total: Total: Invoice Invoice 0.00 0.00 Exploring lightweight event sourcing 15
  • 36. Reloading from history Draft Invoice Invoice Recipient Invoice Item Created Changed Added Recipient: "Erik" Item: "Food" Item amount: 9.95 Total amount: 9.95 apply apply apply Status: Status: Recipient: Status: Recipient: "Draft" "Draft" "Erik" "Draft" "Erik" Total: Total: Total: Invoice Invoice Invoice 0.00 0.00 9.95 Item: Invoice Item "Food" Amount: 9.95 Exploring lightweight event sourcing 15
  • 37. Reloading from history Draft Invoice Invoice Recipient Invoice Item Created Changed Added Recipient: "Erik" Item: "Food" Item amount: 9.95 Total amount: 9.95 apply apply apply Status: Status: Recipient: Status: Recipient: "Draft" "Draft" "Erik" "Draft" "Erik" Total: Total: Total: Invoice Invoice Invoice 0.00 0.00 9.95 Item: Invoice Item "Food" Amount: 9.95 Exploring lightweight event sourcing 15
  • 39. YAGNI? • On the checkout page we’d like to promote products that customers previously removed from their shopping cart. Exploring lightweight event sourcing 16
  • 40. YAGNI? • On the checkout page we’d like to promote products that customers previously removed from their shopping cart. • Can you tell us how many people removed a product but bought it later anyway? Exploring lightweight event sourcing 16
  • 41. YAGNI? • On the checkout page we’d like to promote products that customers previously removed from their shopping cart. • Can you tell us how many people removed a product but bought it later anyway? • ... over the past 5 years? ... and how much time passed in-between? Exploring lightweight event sourcing 16
  • 42. YAGNI? • We have reports of a strange bug, but have not been able to isolate it. Could you look at the production data to find out what’s going on? Exploring lightweight event sourcing 17
  • 43. Is it worth it? Exploring lightweight event sourcing 18
  • 44. Is it worth it? • Make the event sourcing implementation as simple as possible Exploring lightweight event sourcing 18
  • 45. Is it worth it? • Make the event sourcing implementation as simple as possible • ... while avoiding the complexities of databases, ORMs, etc. Exploring lightweight event sourcing 18
  • 46. Is it worth it? • Make the event sourcing implementation as simple as possible • ... while avoiding the complexities of databases, ORMs, etc. • ... unless you want or need them :) Exploring lightweight event sourcing 18
  • 48. Ingredients • Scala case classes Exploring lightweight event sourcing 19
  • 49. Ingredients • Scala case classes • Immutable data structures Exploring lightweight event sourcing 19
  • 50. Ingredients • Scala case classes • Immutable data structures • Single Responsibility Principle (SRP) Exploring lightweight event sourcing 19
  • 51. Ingredients • Scala case classes • Immutable data structures • Single Responsibility Principle (SRP) • Events for durability Exploring lightweight event sourcing 19
  • 52. Ingredients • Scala case classes • Immutable data structures • Single Responsibility Principle (SRP) • Events for durability • Memory Image Exploring lightweight event sourcing 19
  • 54. “CRUD” Create/Update/Delete Exploring lightweight event sourcing 20
  • 55. “CRUD” Create/Update/Delete HTTP POST Validate input and generate event Exploring lightweight event sourcing 20
  • 56. “CRUD” Create/Update/Delete HTTP POST Save Event Validate input and Event generate event Store Exploring lightweight event sourcing 20
  • 57. “CRUD” Create/Update/Delete HTTP POST Save Event Validate input and Event generate event Store Apply Event Status: Recipient: "Draft" "Erik" Invoice Total: 9.95 Item: Invoice Item "Food" Amount: 9.95 Exploring lightweight event sourcing 20
  • 58. “CRUD” Create/Update/Delete HTTP POST Save Event Validate input and Event generate event Store Apply Event Status: Recipient: "Draft" "Erik" Invoice Total: 9.95 Query Render View Item: Invoice Item "Food" Amount: 9.95 Exploring lightweight event sourcing 20
  • 59. “CRUD” Create/Update/Delete HTTP POST Save Event Validate input and Event generate event Store Apply Event Read Status: "Draft" Recipient: "Erik" Invoice Total: 9.95 HTTP GET Query Render View Item: Invoice Item "Food" Amount: 9.95 Exploring lightweight event sourcing 20
  • 60. HTTP POST Save Event Validate input and Event Controller generate event Store Apply Event Status: Recipient: "Draft" "Erik" Invoice Total: 9.95 HTTP GET Query Render View Item: Invoice Item "Food" Amount: 9.95 post("/todo") { val item = field("item", required) map (_.collapseWhitespace) item.fold( error => new ToDoView(toDoItems, Some(error)), value => { record(Created(ToDoItem(UUID.randomUUID(), value))) redirect(url("/todo")) }) } Exploring lightweight event sourcing 21
  • 61. HTTP POST Save Event Validate input and Event Memory Image generate event Store Apply Event Status: Recipient: "Draft" "Erik" Invoice Total: 9.95 HTTP GET Query Render View Item: Invoice Item "Food" Amount: 9.95 case class ToDoItems ( all : Map[String, ToDoItem] = Map.empty, recentlyAdded: Vector[String] = Vector.empty) { def apply(event: Any) = event match { case Created(item: ToDoItem) => copy(all.updated(item.id, item), recentlyAdded :+ item.id) case Updated(item: ToDoItem) => copy(all.updated(item.id, item)) case Deleted(id: String) => copy(all - id, recentlyAdded.filter(_ != id)) } def mostRecent(count: Int) = recentlyAdded.takeRight(count).map(all).reverse } Exploring lightweight event sourcing 22
  • 62. HTTP POST Save Event Validate input and Event View generate event Store Apply Event Status: Recipient: "Draft" "Erik" Invoice Total: 9.95 HTTP GET Query Render View Item: Invoice Item "Food" Amount: 9.95 <table class="zebra-striped"> <thead><tr><th>To-Do</th></tr></thead> <tbody>{ for (item <- toDoItems.mostRecent(20)) yield { <tr><td>{ item.text }</td></tr> } }</tbody> </table> Exploring lightweight event sourcing 23
  • 63. Domain Model Domain Model 2: Save Event 1: Send Command Validate input and generate command Event Store 3: Apply Event 3: Apply Event Status: Recipient: "Draft" "Erik" Invoice Total: 9.95 Item: Invoice Item "Food" Amount: 9.95 Presentation Model Exploring lightweight event sourcing 24
  • 64. Domain Code sealed trait InvoiceEvent case class InvoiceCreated() extends InvoiceEvent case class InvoiceRecipientChanged(recipient: String) extends InvoiceEvent case class InvoiceItemAdded(item: InvoiceItem, totalAmount: BigDecimal) extends InvoiceEvent case class InvoiceSent(sentOn: LocalDate, paymentDueOn: LocalDate) extends InvoiceEvent Exploring lightweight event sourcing 25
  • 65. Domain Code sealed trait InvoiceEvent case class InvoiceCreated() extends InvoiceEvent case class InvoiceRecipientChanged(recipient: String) extends InvoiceEvent case class InvoiceItemAdded(item: InvoiceItem, totalAmount: BigDecimal) extends InvoiceEvent case class InvoiceSent(sentOn: LocalDate, paymentDueOn: LocalDate) extends InvoiceEvent sealed trait Invoice extends AggregateRoot { protected[this] type Event = InvoiceEvent } Exploring lightweight event sourcing 25
  • 66. Domain Code case class DraftInvoice( recipient: Option[String] = None, nextItemId: Int = 1, items: Map[Int, InvoiceItem] = Map.empty) extends Invoice { def changeRecipient(recipient: String): Behavior[DraftInvoice] = ... def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] = ... def send(sentOn: LocalDate): Behavior[Either[String, SentInvoice]] = ... ... private def recipientChanged = when[InvoiceRecipientChanged] { event => copy(recipient = Some(event.recipient)) } private def itemAdded = when[InvoiceItemAdded] { event => copy(nextItemId = event.item.id + 1, items = items + (event.item.id -> event.item)) } private def sent = when[InvoiceSent] { event => SentInvoice(event.sentOn, event.paymentDueOn) } } Exploring lightweight event sourcing 26
  • 67. Domain Code case class DraftInvoice( recipient: Option[String] = None, nextItemId: Int = 1, The “Brains” items: Map[Int, InvoiceItem] = Map.empty) extends Invoice { def changeRecipient(recipient: String): Behavior[DraftInvoice] = ... def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] = ... def send(sentOn: LocalDate): Behavior[Either[String, SentInvoice]] = ... ... private def recipientChanged = when[InvoiceRecipientChanged] { event => copy(recipient = Some(event.recipient)) } private def itemAdded = when[InvoiceItemAdded] { event => copy(nextItemId = event.item.id + 1, items = items + (event.item.id -> event.item)) } private def sent = when[InvoiceSent] { event => SentInvoice(event.sentOn, event.paymentDueOn) } } Exploring lightweight event sourcing 26
  • 68. Domain Code case class DraftInvoice( recipient: Option[String] = None, nextItemId: Int = 1, The “Brains” items: Map[Int, InvoiceItem] = Map.empty) extends Invoice { def changeRecipient(recipient: String): Behavior[DraftInvoice] = ... def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] = ... def send(sentOn: LocalDate): Behavior[Either[String, SentInvoice]] = ... ... The “Muscle” private def recipientChanged = when[InvoiceRecipientChanged] { event => copy(recipient = Some(event.recipient)) } private def itemAdded = when[InvoiceItemAdded] { event => copy(nextItemId = event.item.id + 1, items = items + (event.item.id -> event.item)) } private def sent = when[InvoiceSent] { event => SentInvoice(event.sentOn, event.paymentDueOn) } } Exploring lightweight event sourcing 26
  • 69. Domain Code case class DraftInvoice( recipient: Option[String] = None, nextItemId: Int = 1, items: Map[Int, InvoiceItem] = Map.empty) extends Invoice { def changeRecipient(recipient: String): Behavior[DraftInvoice] = recipientChanged(InvoiceRecipientChanged(recipient)) def addItem(description: String, amount: BigDecimal): Behavior[DraftInvoice] = itemAdded(InvoiceItemAdded(InvoiceItem(nextItemId, description, amount), totalAmount + amount)) def send(sentOn: LocalDate): Behavior[Either[String, SentInvoice]] = if (readyToSend_?) for (updated <- sent(InvoiceSent(sentOn, paymentDueOn = sentOn.plusDays(14)))) yield Right(updated) else pure(Left("invoice not ready to send")) private def totalAmount = items.values.map(_.amount).sum private def readyToSend_? = recipient.isDefined && items.nonEmpty ... } Exploring lightweight event sourcing 27
  • 70. Domain Code case class DraftInvoice( recipient: Option[String] = None, nextItemId: Int = 1, items: Map[Int, InvoiceItem] = Map.empty) extends Invoice { ... protected[this] def applyEvent = recipientChanged orElse itemAdded orElse sent ... } Exploring lightweight event sourcing 28
  • 71. Event Store • Stores sequence of events • Dispatches events when successfully committed • Replays events on startup • It’s your application’s transaction log Exploring lightweight event sourcing 29
  • 72. Conclusion • Event Sourcing captures full historical information • Fully encapsulated domain code decoupled from persistence and presentation layers • Easier to fully understand compared to traditional ORM based approach • Need to learn “Event-Driven” thinking Exploring lightweight event sourcing 30
  • 73. Example Application • https://github.com/erikrozendaal/scala- event-sourcing-example • (soon) https://github.com/zilverline/lessdb- example Exploring lightweight event sourcing 31
  • 74. Implementation Limitations • Single-threaded event store using disruptor pattern • ~ 5.000 commits per second • Number of events to replay on startup • ~ 70.000 JSON serialized events per second • Total number of objects to store in main memory • JVM can run with large heaps (tens of gigabytes) Exploring lightweight event sourcing 32
  • 75. But ready to scale • Advanced event store implementations support SQL, MongoDB, Amazon SimpleDB, etc. • Use persisted view model for high volume objects (fixes startup time and memory usage) • Easy partitioning of aggregates (consistency boundary with unique identifier) • Load aggregates on-demand, use snapshotting Exploring lightweight event sourcing 33
  • 77. References • Example code https://github.com/erikrozendaal/scala-event-sourcing-example • CQRS http://cqrsinfo.com/ • Greg Young, “Unshackle Your Domain” http://www.infoq.com/presentations/greg-young-unshackle-qcon08 • Pat Helland, “Life Beyond Distributed Transactions: An Apostate's Opinion” http://www.ics.uci.edu/~cs223/papers/cidr07p15.pdf • Erik Rozendaal, “Towards an immutable domain model” http:// blog.zilverline.com/2011/02/10/towards-an-immutable-domain-model- monads-part-5/ • Martin Fowler, “Memory Image”, http://martinfowler.com/bliki/ MemoryImage.html • Martin Fowler, “The LMAX Architecture”, http://martinfowler.com/articles/ lmax.html Exploring lightweight event sourcing 35

Notas del editor

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. DDD context: communication between programmers, users and domain experts\nKeep technology out of your domain code!\n
  11. DDD with plain objects would be easy, but we require durability. No one likes to be told &amp;#x201C;don&amp;#x2019;t turn off the system, it will lose data!&amp;#x201D;\n
  12. But if durability is so important, why do current systems forget so much?\n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. Not a problem if implementation difficulty only affected implementers, but:\n- lazy/not lazy\n- select 1+n\n- large sessions\n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. case classes: events, etc\nimmutable data structures: isolation, safe-sharing, to replace copying/locking by ORM/DB\nSRP: composing our system from simpler parts\n
  72. case classes: events, etc\nimmutable data structures: isolation, safe-sharing, to replace copying/locking by ORM/DB\nSRP: composing our system from simpler parts\n
  73. case classes: events, etc\nimmutable data structures: isolation, safe-sharing, to replace copying/locking by ORM/DB\nSRP: composing our system from simpler parts\n
  74. case classes: events, etc\nimmutable data structures: isolation, safe-sharing, to replace copying/locking by ORM/DB\nSRP: composing our system from simpler parts\n
  75. case classes: events, etc\nimmutable data structures: isolation, safe-sharing, to replace copying/locking by ORM/DB\nSRP: composing our system from simpler parts\n
  76. \n
  77. \n
  78. \n
  79. \n
  80. \n
  81. \n
  82. \n
  83. \n
  84. \n
  85. \n
  86. \n
  87. \n
  88. \n
  89. No infrastructure stuff on events (mandatory interface or fields)\n
  90. Pure! Behavior&amp;#x2019;s are like Hibernate&amp;#x2019;s Session or JPA&amp;#x2019;s EntityManager\n- access current aggregates, track changes (events), ... but return a result + monad + flatMap etc.\n- why split? because we need to load from history without re-executing business logic\n- Return type of &amp;#x201C;send&amp;#x201D;, &amp;#x2018;when&amp;#x2019; builds Event =&gt; Behavior[A] functions\n
  91. Pure! Behavior&amp;#x2019;s are like Hibernate&amp;#x2019;s Session or JPA&amp;#x2019;s EntityManager\n- access current aggregates, track changes (events), ... but return a result + monad + flatMap etc.\n- why split? because we need to load from history without re-executing business logic\n- Return type of &amp;#x201C;send&amp;#x201D;, &amp;#x2018;when&amp;#x2019; builds Event =&gt; Behavior[A] functions\n
  92. Pure! Behavior&amp;#x2019;s are like Hibernate&amp;#x2019;s Session or JPA&amp;#x2019;s EntityManager\n- access current aggregates, track changes (events), ... but return a result + monad + flatMap etc.\n- why split? because we need to load from history without re-executing business logic\n- Return type of &amp;#x201C;send&amp;#x201D;, &amp;#x2018;when&amp;#x2019; builds Event =&gt; Behavior[A] functions\n
  93. Pure! Behavior&amp;#x2019;s are like Hibernate&amp;#x2019;s Session or JPA&amp;#x2019;s EntityManager\n- access current aggregates, track changes (events), ... but return a result + monad + flatMap etc.\n- why split? because we need to load from history without re-executing business logic\n- Return type of &amp;#x201C;send&amp;#x201D;, &amp;#x2018;when&amp;#x2019; builds Event =&gt; Behavior[A] functions\n
  94. \n
  95. \n
  96. \n
  97. \n
  98. \n
  99. \n
  100. \n
  101. \n
  102. \n
  103. Implementation is not trivial! But I expect all members of the team to be able to understand each component (unlike ORM)\n
  104. \n
  105. \n
  106. \n
  107. \n
  108. \n