Slide 1

Slide 1 text

Model-Driven Design with Business Patterns Explained in Scala - REA Model in CQRS/Event Sourcing - かとじゅん( ) Scala Matsuri 2020 2020/10/17 Scalaで解説する、ビジネスパターンによるモデル駆動設計 CQRS/Event SourcingにおけるREAモデルとは @j5ik2o 1

Slide 2

Slide 2 text

Who am I? Chatwork's Tech Lead. Recent Activities My job is building and testing CQRS/Event Sourcing system by akka-cluster. Published a thin Scala book in zenn.dev I have taken the following courses in and have been certified 誰? @j5ik2o 楽しいScala 気軽にはじめてみよう Lightbend Academy 2

Slide 3

Slide 3 text

Agenda "Model-Driven Design Using Business Patterns" by Pavel Hruby, published in 2006, is a book that provides useful patterns related to domain-driven design. The book introduces a universal modeling pattern for business applications called REA (Resource, Event, Agent). I'd like to discuss some excerpts from REA. 3

Slide 4

Slide 4 text

Agenda In Model-Driven Design Using Business Patterns: REA Structure Pattern Behavior Patterns REA構造パターンと振る舞いパターンを紹介します 4

Slide 5

Slide 5 text

REA Structure Pattern 5

Slide 6

Slide 6 text

About REA REA = Resource, Event, Agent Events mean things that happens in past, Reources mean non-event objects Agents mean people. REAとは 6

Slide 7

Slide 7 text

The REA structure consists of Policy Level Operational Level 7

Slide 8

Slide 8 text

Economic {Resources, Agents, Events} at Operational Level Economic Resources are scarce and useful to economic agents. The targets that you want to plan, monitor, and control. Economic Agents are individuals or organizations that can control economic resources and transfer control to individuals or organizations, or take control from others. Economic Events are those that represent an increase or decrease in the value of the economic resources controlled by a company. Some of them occur over time, such as sales of goods that occur immediately, rentals, and services. Event Resource Agent inflow outflow stockflow 経済リソース、経済エージェント、経済イベント 8

Slide 9

Slide 9 text

Commitments, Contracts at Policy Level コミットメント、契約 A Contract is a collection of covenants with increasing and decreasing commitments. Based on the terms and conditions specified by the covenants, the contract may create further commitments. Penalties for failure to fulfill commitments can be expressed in the covenants A Commitment is a promise or obligation of an economic agent to perform some economic event in the future. A statement of order item represents a commitment to sell goods Contract Commitment Event Resource Agent stipulation fulfillment related_party reservation inflow outflow stockflow inflow outflow 9

Slide 10

Slide 10 text

The Basic Concept of the REA Model If a company wants to increase the overall value of the resources under its control, it usually has to reduce the value of some of the resources REAモデルの基本的な考え⽅ 10

Slide 11

Slide 11 text

Two Patterns of Increase or Decrease in Resources Exchange The process by which a company(as economic agent) receives economic resources from another economic agent and gives those economic resources to that economic agent in return Trading is an exchange process Conversion The process by which a company(as economic agent) uses or consumes economic resources for the production of new economic resources or the modification of existing economic resources. Production of goods is a conversion process 経済リソースの2つの増減パターン 11

Slide 12

Slide 12 text

In the case, Sushi Restaurant すし屋の場合 12

Slide 13

Slide 13 text

In the case, Sushi Restaurant Customers, vendors, employees and sushi restaurant are economic agents. Sushi, cash, labor force, and raw materials for sushi are economic resources There are three parts to the transaction process: sales, purchases, and labor force acquisition. <> Sushi Production <> Labor Procurement <> Sales <> Purchase すし屋の場合 13

Slide 14

Slide 14 text

The Sales Process of a Sushi Restaurant Selling sushi is the process of exchanging sushi for cash Sushi and cash are economic resources The economic events that occur are Transfer of sushi ownership from the sushi shop to the customer, economic decline event that sells the product Transfer of cash ownership from the customer to the sushi shop, economic increase event of receiving cash <> Sale すし屋の販売プロセス 14

Slide 15

Slide 15 text

REA Model for Sushi Sales The sales process is represented without contracts and commitments The customer supplies the cash and the sushi shop receives the cash. Sales are made by the sushi shop, where the sushi is supplied and received by the customer «agent» SushiRestaurant «resource» Money «increase_event» CashReceipted «agent» Customer «decrease_event» SushiSold «resource» Sushi «exchange» «inflow» «supply» «receive» «outflow» «receiver» «supply» すし販売のREAモデル 15

Slide 16

Slide 16 text

Events Implementation イベントの実装 Separating events into two, such as and , allows you to separate payment and sales customers, or to manage sales and cash receipts of goods that occur at different times. Chronologically, the receipt of cash will always occur after the sale of the sushi. In this design, cash cannot be received first. If support the pre-order, need to revise the design. // A Sushi was sold case class SushiSold( id: SushiSaledId, toCustomerId: CustomerId, sushiType: SushiType, quantity: SushiQuantity, occurredAt: Instant ) // Cash was receipted case class CashReceipted( id: CashReceiptedId, sushiSaledId: SushiSaledId, fromCustomerId: CustomerId, amount: Money, occurredAt: Instant ) 16

Slide 17

Slide 17 text

Expressing Rights Related to Economic Resources Inflow The relationship between increased economic events and resources is called an inflow Outflow The relationship between decreasing economic events and resources is called an outflow. 経済リソースに関連する権利を表現する «resource» Book «increase_event» BookBorrowed «agent» Library «agent» Reader Right to Read«inflow» 1 0..* Lender«supply» 1 0..* Borrower«receipt» 0..* 1 case class Book(id: BookId, name: BookName, ...) { // ... } case class BookBorrowed( id: BookBorrowedId, bookId: BookId, lenderId: OwnerAccountId, borrowerId: UserAccountId ) 17

Slide 18

Slide 18 text

REA model of the sales process with sales orders 販売注⽂を伴う販売プロセスのREAモデル is a contract and are commitments Commitments and events represent a prefactual relationship «contract» SalesOrder «increase_commitment» Payment «decrease_commiment» Sales «agent» Customer «agent» SushiRestraunt «resource» Money «increase_event» CashReceipted «decrease_event» SushiSold «resource» Sushi «related_party» «related_party» «stipulations» «stipulations» «interdependence_of_exchange» «reservation» «reservation» «exchange_duality» «inflow» «receive» «outflow» «receive» «supply» «supply» 18

Slide 19

Slide 19 text

The Concept of Reservations Commitment expresses what resources are associated with the economic events that occur later «commitment» RentalCommitment Line itemized lease agreement, etc. «event» RenatalEvent Actual use of the apartment «resource» Apartment Fulfillment 0..* 0..* Rental reservation the right to use 0..* 1..* Inflow move the right to use 1 0..* 予約という概念 19

Slide 20

Slide 20 text

When reserving a resource type リソースタイプを予約する場合 If the commitment involves a resource type, it also involves an actual resource that matches the resource type at some point in the future. This is called reservation assignment. In hotel operations, reservations are related to room specifications until the guest's arrival date, but on the morning of arrival, the front desk clerk assigns a room number to each reservation beginning that day. ResourceType Resource DecreaseCommitment DecreaseEvent Reservation① Specification Outflow Fulfillment Reservation② 20

Slide 21

Slide 21 text

Which Object is the Aggregate in the Reservation? ところで、予約における集約はどのオブジェクト? 21

Slide 22

Slide 22 text

What is the model for the current state? Commitment is a promise that a future event will happen and Event is something that happened in the past. How do you model an aggregate that represents the current state? «resource_type» RoomPlan «resource» Room «commitment» BorrowRoom «event» RoomBorrowed Reservation① Specification Outflow Fulfillment Reservation② 現在の状態を表すモデルとは 22

Slide 23

Slide 23 text

FYI: Event Storming(Design Level) A powerful domain analysis technique based on the domain events. 1. Find the command that generates the event. 2. Find an aggregate to receive that the command. User Event Command Aggregate Policy ReadModel UI invoked on generates translated into triggers invokes It's actually a CQRS/Event Sourcing system イベントストーミング(デザインレベル) 23

Slide 24

Slide 24 text

REA Model in CQRS/Event Sourcing system The Commitment raises an Event; in Event Storming be called it the Command. Find the Aggregate from that the Commands. Event CommandAsCommitment Aggregate invoked on generates ⬇ BorrowRoom RoomBorrowed RoomReservation invoked on generates 24

Slide 25

Slide 25 text

RoomReservation Aggregate Design RoomReservation is an aggregate that represents a room reservation If the Commands is accepted, the Events will be generated «resource_type» RoomPlan «resource» Room «agent» Customer «cmd» BorrowRoom «cmd_reply» BorrowRoomReply «event» RoomBorrowed «aggregate» RoomReservation id: RoomReservationId RoomPlanReservation def borrowRoom(borrowRoom: BorrowRoom): (RoomInstanceReservation, RoomBorrowed) RoomInstanceReservation invoked on generates customerId roomPlanId roomId specification RoomReservation集約の設計 25

Slide 26

Slide 26 text

Examples of Reserved Objects(1/2) 予約の実装例(1/2) // 部屋 case class RoomPlan { id: RoomPlanId, name: RoomTypeName, roomType: RoomType, // Single, Double, ... maxCapacity: MaxCapacity withBreakfast: Boolean, smokingAllowed: Boolean } // 部屋 case class Room { id: RoomId, roomNumber: RoomNumber, // ... } // Borrow Room Commitment case class BorrowRoom( id: BorrowRoomId, roomPlanId: RoomPlanId, borrowDate: LocalDate, occurredAt: Instant ) // Room Borrowed Event case class RoomBorrowed( id: RoomBorrowedId, commitmentId: BorrowRoomId, roomId: RoomId, borrowedDate: LocalDate, occurredAt: Instant ) 26

Slide 27

Slide 27 text

Examples of Reserved Objects(2/2) 予約の実装例(2/2) abstract class RoomReservation( id: RoomReservationId, customerId: CustomerId, roomPlanId: RoomPlanId, borrowDate: LocalDate, numOfNights: NumOfNights, createAt: Instant ) { ... } class RoomPlanReservation(...) exntends RoomReservation( id, customerId, roomPlanId, roomId, borrowDate, numOfNights, createAt) { // borrow room def borrowRoom(borrowRoom: BorrowRoom) : (RoomReservation, RoomBorrowed) = { require(roomPlan.id == roomPlanId) require(roomPlan.isSatisfiedBy(room)) (new RoomReservation(this, room.id), new RoomBorrowed(RoomBorrowedId(), id, room.id, borrowedDate, Instant.now())) } class RoomReservation( id: RoomReservationId, customerId: CustomerId, roomPlanId: RoomPlanId, roomId: RoomId, borrowDate: LocalDate, numOfNights: NumOfNights, createAt: Instant) exntends RoomReservation( id, customerId, roomPlanId, roomId, borrowDate, numOfNights, createAt) { ... } 27

Slide 28

Slide 28 text

Example for Borrow Room UseCase, In State Sourcing... This is an example of implementation that looks good at first glance, but it's Double Committing with the state and the event... If only the fails, the complex actions to guarantee failure is needed, such as undoing the state update by the or retrying until the succeeds. To avoid these problems, be introduced Event Sourcing. Event Sourcing will be discussed next time. ユースケースの実装例 class BorrowRoomUseCase(...) { def execute(...): Future[Unit] = { val borrowRoom = ... for { roomReservation <- roomReservationRepository.findBy(borrowRoom.roomReservationId) (boomReservationUpdated, roomBorrowed) <- roomReservation.borrowRoom(roomCommitmente) _ <- roomReservationRepository.store(boomReservationUpdated) _ <- eventPublisher.publish(roomBorrowed) } yield () } } 28

Slide 29

Slide 29 text

Behavior Patterns 29

Slide 30

Slide 30 text

DueDate Pattern According to the description in the book... The DueDate is a value object representing a due date Determine if the specified due date has passed There are state transitions such as active, expired, completed, and canceled. DueDate value: LocalDate isDue(): Boolean confirm(l: Option[Task => Unit]): Unit complete(l: Option[Task => Unit]): Unit cancel(l: Option[Task => Unit]): Unit Active Expired Completed Canceled 期⽇パターン 30

Slide 31

Slide 31 text

Refactoring The Design!!! Express only whether the due date is past due without having a state. The state to which the due date relates is the state of the task. Tasks should be modeled separately. The transition to the new state should be represented by a new instance(immutable) 設計の⾒直し DueDate value: LocalDate isDue(): Boolean confirm(l: Option[Task => Unit]): Unit complete(l: Option[Task => Unit]): Unit cancel(l: Option[Task => Unit]): Unit Active Expired Completed Canceled ⇰ Task id: TaskId status: TaskStatus assignee: EmployeeId assigner: EmployeeId description: TaskDescription dueDate: DueDate confirm(l: Option[Task => Unit]): Task complete(l: Option[Task => Unit]): Task cancel(l: Option[Task => Unit]): Task toDueDate(): DueDate Active Expired Completed Canceled DueDate value: LocalDate isDue(): Boolean 31

Slide 32

Slide 32 text

DueDate with Task Pattern 期⽇とタスクのパターン Task id: TaskId status: TaskStatus assignee: EmployeeId assigner: EmployeeId description: TaskDescription dueDate: DueDate confirm(l: Option[Task => Unit]): Task complete(l: Option[Task => Unit]): Task cancel(l: Option[Task => Unit]): Task toDueDate(): DueDate Active Expired Completed Canceled DueDate value: LocalDate isDue(): Boolean The Due Date is a value object representing a due date.It determines if the specified due date has passed. To make tasks, TODOs, etc., hold Due Dates as targets related to Due Dates, so that they can determine when they should be completed by State transitions such as valid, due date, completed, and canceled may be considered for the Due Date 32

Slide 33

Slide 33 text

Example Implementation of DueDate A value object to determine if it is before or after a deadline in isAfter/isBefore In the book, DueDate included state management, but we have separated that responsibility into task objects. 期⽇の実装例 final case class DueDate(dueDate: LocalDate, zoneOffset: ZoneOffset) { private val dueAsInstant = dueDate.atStartOfDay().toInstant(zoneOffset) def toLocalDate: LocalDate = dueDate def isAfter(instant: Instant): Boolean = { instant.isAfter(dueAsInstant) } def isBefore(instant: Instant): Boolean = { instant.isBefore(dueAsInstant) } } 33

Slide 34

Slide 34 text

Example Implementation of Task タスクの実装例 State starts from Active When the due date comes, the state will transition to Due final case class Active private[sub] (id: TaskId, assignee: EmployeeId, assigner: EmployeeId, description: TaskDescription, dueDate: DueDate) extends Task { override def status: TaskStatus = TaskStatus.Active override def confirm(instant: Instant, listener: Option[Task => Unit]): Task = { // if it's overdue if (dueDate.isAfter(instant)) { // create a due date state task val newTask = Due(id, assignee, assigner, description, dueDate) li f h k Complete tasks or Cancel tasks Pattern match override def complete(listener: Option[Task => Unit]): Task = { val newTask = Completed(id, assignee, assigner, description, dueDate) listener.foreach(_(newTask)) newTask } override def cancel(listener: Option[Task => Unit]): Task = { val newTask = Canceled(id, assignee, assigner, description, dueDate) listener.foreach(_(newTask)) newTask } task match { case Task.Expired(_, assignee, assigner, _, _) => // ... case _ => // ... } 34

Slide 35

Slide 35 text

Conclusion Although the Japanese edition is out of print, it is a valuable guidebook. Recommended for the following points. REA is the gateway to modeling. REA is the hints for CQRS/ES The behavior of the value object is also helpful. It is even better if you can use the guidebook as a reference for practical applications. まとめ 35

Slide 36

Slide 36 text

The End 終わり 36