backend software engineer at ChatWork: ◦ Tech lead, Architect, DDD evangelist ◦ ChatWork: the largest "business chat" service in Japan 自己紹介。ChatWorkでエンジニアとして働いています
Actor and DDD.The objective is to eliminate the architecture overhead. The references are in the followings: ◦ Vaughn Vernon: Reactive Domain-Driven Design - InfoQ ◦ Reactive DDD with Scala and Akka • The benefits are not only being Message-driven, but also Resilient, Responsive, and Elastic.Reactive-DDD may be Reactive System + DDD? • In Scala, Akka is one of the best options to construct CQRS+ES system based on DDD リアクティブDDDについて
can deposit and withdraw money ◦ Deposit to a bank account ◦ Withdraw from a bank account ◦ Refer to transaction(deposit or withdraw) histories • Source ◦ https://git.io/reactive-ddd-example 銀行口座APIサーバの例 // Deposit to a bank account $ curl -X PUT \ http://localhost:8080/bank-accounts/XEe/events \ -H 'content-type: application/json' \ -d '{ "type": "deposit", "amount": 1000, "currencyCode": "JPY" }' {"id":"XEe","errorMessage":null}% // Refer to transaction histories on a bank account $ curl -X GET \ http://localhost:8080/bank-accounts/XEe \ -H 'content-type: application/json' { "id": "XEe", "values": [ { "type": "deposit", "amount": 1000, "currencyCode": "JPY", "createAt": 1520219459 } ], "errorMessage": null }
and Read • Write - Read Flow ◦ API(Write) →WriteDB→RMU→ReadDB→ API(Read) • Actor based implementation 想定するシステム構成 Write DB Read DB API RMU Command Query Domain Event Read Model Domain Event SQL
in requirements ◦ A customer wants to deposit to his bank account, or withdraw from it ◦ A customer wants to refer to transaction histories in his bank account • Domain model candidates ? ◦ Customer ◦ BankAccount ◦ Deposit, Withdraw ◦ TransactionHistory • Analyze the domain model from use cases ドメインモデルを探索する Customer BankAccount Deposit Withdraw Transaction History
bank account ◦ In deposit screen(B), a customer(A) ▪ puts money in ATM. ▪ inputs(C) amount(E) of money(E). ▪ clicks(C) confirm button. ◦ The system(A) ▪ confirms(C) money(E). ▪ deposits(C) it to his own bank account(E). ▪ saves(C) his own bank account(E) state. • Same in withdraw case ユースケース分析(1/2) • Annotations ◦ A = Actor in use-case ◦ BCE stereotypes ▪ B = Boundary, interfaces for actor ▪ C = Control, software-functions ▪ E = Entity, domain objects
bank acount ◦ A customer(A) ▪ puts(C) his own bank book(E) in ATM ▪ clicks(C) button for it ◦ The system ▪ reads(C) transaction histories(E) in his own bank account(E) ▪ updates(C) the bank book(E) Use case analysis(2/2) ユースケース分析(2/2) • How to analyze BCE ◦ Nouns are domain model candidates, or the ‘properties’ ◦ Verbs tend to be 'methods' or 'relations' • Terms not subject to systematization are irrelevant • UI and I/O elements are irrelevant as well
▪ Bank Account ID ▪ Current Balance(unnecessary?) ◦ Transaction History(E) ▪ Occurred Date ▪ Deposit or Withdraw type ▪ Amount of money 注目すべき分析要素 • Control candidates ◦ Deposit ◦ Withdraw
◦ maintain consistency, suppress direct relations to increase. • Boundary ◦ All objects that cannot exist without a bank account are included in Bank Account Aggregate. ◦ Is Transaction History Aggregate? Or is it an object inside Aggregate? ◦ Obviously a search for transaction history requires a bank account, but Aggregate with whole histories may consume huge memories. In this case, prefer on-demand DB queries to object references 集約境界の定義 BankAccount BankAccountId BankAccountName balance: Money BankAccountEvent BankAccountEventId Deposit or Withdraw amount: Money occurredAt: DateTime ?
responsibility from Aggregate, Aggregate concentrates on command responsibility. • Domain events that occurred in Aggregate are persisted and are used for query responsibilities • Not necessary to include BankAccountEvent by this responsbility separation. state transition Command side Query side BankAccountEvent BankAccountEventRecord RMU Dao, Record Write DB R/W separation Read DB
Controls ◦ BankAccount and Deposit/Withdraw • Controls are analyzed as the message to Entities • Then find the appropriate Entity to receive the Control ◦ Deposit/Withdraw is sent to BankAccount • This analysis is reflected to code ◦ bankAccount.deposit(amount, …) ◦ bankAccountRef ! Deposite(amount, ...) ドメインモデルへの振る舞いの割り当て Deposit or Withdraw Bank Account Transition state
behavior concentration to entities.But does BankAccount itself work? • How did Alexander analyze the kettle? ◦ A kettle is just a container to store water? ◦ He analyzed the object to inform the boiling of water • The analysis result changes depending on what kind of responsibility is given to the object 銀行口座自身が動作するのですか? just a container to store water? An object to inform the boiling of water
simpler than the traditional layer style • Direct dependency from outside to inside • If use interface from use case, use indirect dependency • Layers ◦ Interface layer ▪ HTTP, DB, Aggregates ◦ Use case layer ▪ Roles to combine tasks for workflow ◦ Domain layer ▪ domain objects レイヤ化アーキテクチャ
one of approaches to the layered architecture プロジェクト構成の例 interface use-case domain infrastructure controller write use case read use case domain object aggregate dao, record port port(impl) port port(impl) command stack query stack
of Aggregate • Domain objects are simple objects represented by case classes and basic data types • The internal state encapsulation by using Actors ◦ The internal state can only be manipulated via messages ドメインオブジェクトと集約の関係 BankAccountAggregate(akka-actor) state: BankAccount(Entity) id: BankAccountId(VO) name: BankAccountName(VO) balance: Money(VO)
Actor(stateful actor) • Create a initial state by `OpenRequest` • Use `DepositRequest` or `WithdrawRequest` to update the state. Also return the latest state for assertion. • After receiving the command, change the state and persists the domain events • Use Domain Events for Actor replaying 集約の設計 BankAccountAggregate BankAccount DepositeRequest DepositSucceede d Opened Deposited Withdraw change the state persist domain events return the response Append only persist replay
actor replaying but also for systems integration (e.g. Read Model Updater) • Use Domain event names in terms of domain knowledge, not terms for persistence ◦ Opened ◦ Deposited ◦ Withdrawn ◦ Closed ドメインイベントの設計
is domain object • persistenceId is a string value combine aggregate name and id • Release system resource by receive timeout 集約の例(1/5) aggregate-name + id number Handle the state of domain object
Events • Complete recovery when `RecoveryCompleted` is received • Accelerate actor replaying by using events and the latest snapshot. • When SnapshotOffer, overwrite the state completely 集約の例(4/5)
akka-persistence provides entity writing functions ◦ ActorSystem(#actorSelection, #actorOf) provides entity reading functions ◦ Other query methods are in the query stack • In-memory states act as cache ◦ No need to poll the DB because the Actor keeps the latest state ◦ Events is appended to journals, the state is restored from the log at restart • However, the scaling strategy for stateful actors is required. For example use the following: ◦ akka-cluster, akka-cluster-sharding • In this time, use akka-cluster-sharding akka-persistenceのメリット
Shard by akka-cluster-sharding • Start ShardRegion on each node. Use it to forward command to stateful actors • Use ShardRegion.ExtractEntityId to extract the entity id from the command • Use ShardRegion.ExtractShardId to extract the shard id from command
caller. It acts like a proxy • Start ShardRegion when proxy is created • Forward the received message to ShardRegion • Note that it uses ClusterSharding# start instead of ActorSystem#actorOf
cases • the methods are a combination of akka-stream and Aggregate • The use case is pipe-line to execute tasks, Not I/O responsebility • I/O is supported by interface ports 集約用ユースケースの設計 Interface Layer UseCase Layer BankAccountAggregateFlowsImpl BankAccountAggregateFlows BankAccountAggregateUseCase BankAccountAggregates Domain Layer BankAccount ShardRegion BankAccountAggregate Output port
support command operations only • Enqueue requests and `Promise` object for response • Promise completes after processing the request • Use Aggregate via interface ports
is Request and Promise[Response] • Execute sink to complete Promise after Aggregate flow Use Aggregate via interface ports The openBankAccountQueue is a long running stream
parameter types and return types of the methods depends on Use case layer • However the implementation depend on interface layer • Use ask pattern to combine Aggregate in the stream easily. Interface port(I/F) in use case ask to Aggregate
events リードモデルの例 generate read-models auto generated using septeni_orignal/sbt-dao-generator Persist the latest sequence number in consideration of process stop
route invokes use case methods • No ports because direct dependency is available コントローラの設計 Interface Layer UseCase Layer BankAccountReadModelUseCase Routes extends BankAccountController BankAccountAggregateUseCase
to build read-models • Note: ◦ Should distribute the pid in charge if RMU num < pid num ◦ Should reassign pid responsible for the failed node to another node • This talk ignores the above issues, and shows a simple case as follows: ◦ 1 Domain Event Type : 1 RMU instance. リードモデルアップデータの設計 pid = 1 pid = 2 Aggregate1 Aggregate2 RMU record1 record2 Handle specific domain event type Write DB Read DB
stream • `bankAccountReadModelFlows` and `journalReader` are ports for reading • First, the latest sequence number is obtained from the DB, consume the events using the tag name • In `projectionFlow`, the read-model is updated based on the domain events リードモデル用ユースケースの例
The main tools used are as follows: ◦ akka-actor, akka-persistence, akka-cluster-sharding, akka-stream, akka-http • Reactive-DDD gets a advantage as compared to non Reactive-DDD, because it benefits both reflection of domain knowledge into code and non-functional requirements. • This book is recommended to learn the basics of Akka. まとめ