マイクロサービス ドメイン モデルの設計Design a microservice domain model

ビジネス マイクロサービスまたは有界コンテキストごとに 1 つのリッチ ドメイン モデルを定義します。Define one rich domain model for each business microservice or Bounded Context.

ここでの目標は、ビジネス マイクロサービスまたは有界コンテキスト (BC) ごとに 1 つのまとまりのあるドメイン モデルを作成することです。Your goal is to create a single cohesive domain model for each business microservice or Bounded Context (BC). ただし、BC またはビジネス マイクロサービスは、1 つのドメイン モデルを共有する複数の物理サービスから構成される場合があることに注意してください。Keep in mind, however, that a BC or business microservice could sometimes be composed of several physical services that share a single domain model. ドメイン モデルは、それが表す 1 つの有界コンテキストまたはビジネス マイクロサービスのルール、ビヘイビアー、ビジネス言語、および制約をキャプチャする必要があります。The domain model must capture the rules, behavior, business language, and constraints of the single Bounded Context or business microservice that it represents.

ドメイン エンティティ パターンThe Domain Entity pattern

エンティティは、ドメイン オブジェクトを表すもので、主にその ID、継続性、および永続化によって時間とともに定義されます。エンティティを構成する属性によってのみ定義されるわけではありません。Entities represent domain objects and are primarily defined by their identity, continuity, and persistence over time, and not only by the attributes that comprise them. Eric Evans 氏が言うように、"主にその ID で定義されるオブジェクトは、エンティティと呼ばれます"。As Eric Evans says, "an object primarily defined by its identity is called an Entity." エンティティは、モデルの基礎となるため、ドメイン モデルで非常に重要です。Entities are very important in the domain model, since they are the base for a model. したがって、エンティティは、慎重に識別し、設計する必要があります。Therefore, you should identify and design them carefully.

エンティティの ID は、複数のマイクロサービスまたは有界コンテキストを横断できる。An entity's identity can cross multiple microservices or Bounded Contexts.

同じ ID (つまり、同じドメイン エンティティではない可能性がありますが、同じ Id 値) を複数の有界コンテキストまたはマイクロサービス全体でモデリングできます。The same identity (that is, the same Id value, although perhaps not the same domain entity) can be modeled across multiple Bounded Contexts or microservices. ただし、同じ属性とロジックを持つ同じエンティティが複数の有界コンテキストに実装されるわけではありません。However, that does not imply that the same entity, with the same attributes and logic would be implemented in multiple Bounded Contexts. 代わりに、それぞれの有界コンテキストのエンティティは、その属性と動作を有界コンテキストのドメインで必要なものに制限します。Instead, entities in each Bounded Context limit their attributes and behaviors to those required in that Bounded Context's domain.

たとえば、バイヤー エンティティは、プロファイルまたは ID マイクロサービスのユーザー エンティティで定義された個人の属性 (ID など) の大部分を持っている可能性があります。For instance, the buyer entity might have most of a person's attributes that are defined in the user entity in the profile or identity microservice, including the identity. しかし、注文マイクロサービスのバイヤー エンティティは、持っている属性が少ない可能性があります。注文プロセスに特定のバイヤー データしか関連付けられていないからです。But the buyer entity in the ordering microservice might have fewer attributes, because only certain buyer data is related to the order process. 各マイクロサービスまたは有界コンテキストのコンテキストは、そのドメイン モデルに影響を与えます。The context of each microservice or Bounded Context impacts its domain model.

ドメイン エンティティは、データ属性を実装するだけでなく、ビヘイビアーも実装する必要があります。Domain entities must implement behavior in addition to implementing data attributes.

DDD のドメイン エンティティは、エンティティ データ (アクセスされるメモリ内のオブジェクト) に関連するドメイン ロジックまたはビヘイビアーを実装する必要があります。A domain entity in DDD must implement the domain logic or behavior related to the entity data (the object accessed in memory). たとえば、注文エンティティ クラスの一部として、ビジネス ロジックと操作を、タスク (注文項目の追加、データ検証、合計計算など) のメソッドとして実装する必要があります。For example, as part of an order entity class you must have business logic and operations implemented as methods for tasks such as adding an order item, data validation, and total calculation. エンティティのメソッドでは、エンティティのルールをアプリケーション レイヤー全体に広げるのではなく、エンティティの不変条件とルールに対処します。The entity's methods take care of the invariants and rules of the entity instead of having those rules spread across the application layer.

図 7-8 は、データの属性だけではなく、操作やメソッドを、関連するドメイン ロジックとともに実装するドメイン エンティティを示しています。Figure 7-8 shows a domain entity that implements not only data attributes but operations or methods with related domain logic.

ドメイン エンティティのパターンを示す図。

図 7-8Figure 7-8. データとビヘイビアーを実装するドメイン エンティティ設計の例Example of a domain entity design implementing data plus behavior

ドメイン モデル エンティティでは、メソッドからビヘイビアーを実装します。つまり、"貧血症" モデルではありません。A domain model entity implements behaviors through methods, that is, it's not an "anemic" model. もちろん、エンティティ クラスの一部としてどのロジックも実装しないエンティティを持つ場合もあります。Of course, sometimes you can have entities that do not implement any logic as part of the entity class. この状況は、大部分のロジックが集約ルートで定義されており、子エンティティが特別なロジックを持たない場合に、集約内の子エンティティで発生する場合があります。This can happen in child entities within an aggregate if the child entity does not have any special logic because most of the logic is defined in the aggregate root. ドメイン エンティティではなく、サービス クラスに多数のロジックが実装されている複雑なマイクロサービスがある場合は、ドメイン モデル貧血症になる場合があります。ドメイン モデル貧血症については、次のセクションで説明します。If you have a complex microservice that has a lot of logic implemented in the service classes instead of in the domain entities, you could be falling into the anemic domain model, explained in the following section.

リッチ ドメイン モデルと貧血症ドメイン モデルの比較Rich domain model versus anemic domain model

Martin Fowler は、投稿した AnemicDomainModel で、ドメイン モデル貧血症について、次のように述べています。In his post AnemicDomainModel, Martin Fowler describes an anemic domain model this way:

ドメイン モデル貧血症の基本的な症状は、一見、それが本物のドメイン モデルに見えるという点です。The basic symptom of an Anemic Domain Model is that at first blush it looks like the real thing. オブジェクトがいくつかあり、それらはドメイン空間にある名詞から名前をつけられています。それから、オブジェクト同士がしっかりとしたリレーションで結びついており、本物のドメイン モデルと同じような構造を持っているのです。There are objects, many named after the nouns in the domain space, and these objects are connected with the rich relationships and structure that true domain models have. ただし、オブジェクトのビヘイビアーを見れば違いがわかります。それらのオブジェクトにはわずかなビヘイビアーしかなく、ゲッターとセッターの入れ物にすぎないということに気づくと思います。The catch comes when you look at the behavior, and you realize that there is hardly any behavior on these objects, making them little more than bags of getters and setters.

もちろん、貧血症ドメイン モデルを使用する場合、これらのデータ モデルは、すべてのドメインまたはビジネス ロジックをキャプチャする一連のサービス オブジェクト (従来名は "ビジネス レイヤー") から使用されます。Of course, when you use an anemic domain model, those data models will be used from a set of service objects (traditionally named the business layer) which capture all the domain or business logic. ビジネス レイヤーは、データ モデル上にあり、データ モデルを単にデータとして使用します。The business layer sits on top of the data model and uses the data model just as data.

貧血症ドメイン モデルは、手続き型の設計にすぎません。The anemic domain model is just a procedural style design. 貧血症エンティティ オブジェクトは、ビヘイビアー (メソッド) がないため、本当のオブジェクトではありません。Anemic entity objects are not real objects because they lack behavior (methods). また、データ プロパティを保持するだけなので、オブジェクト指向設計ではありません。They only hold data properties and thus it is not object-oriented design. すべてのビヘイビアーをサービス オブジェクト (ビジネス レイヤー) に入れると、どうしても最後にはスパゲッティ コードトランザクション スクリプトが出来上がり、その結果、ドメイン モデルが持つ利点が失われます。By putting all the behavior out into service objects (the business layer) you essentially end up with spaghetti code or transaction scripts, and therefore you lose the advantages that a domain model provides.

とにかく、マイクロサービスまたは有界コンテキストが非常にシンプル (CRUD サービス) である場合は、エンティティ オブジェクト形式でデータ プロパティしか持たない貧血症ドメイン モデルで十分であり、より複雑な DDD パターンを実装する価値はないかもしれません。Regardless, if your microservice or Bounded Context is very simple (a CRUD service), the anemic domain model in the form of entity objects with just data properties might be good enough, and it might not be worth implementing more complex DDD patterns. その場合、CRUD を目的としてデータしか持たないエンティティを意図的に作成したので、これは単に永続化モデルになります。In that case, it will be simply a persistence model, because you have intentionally created an entity with only data for CRUD purposes.

このため、有界コンテキストにもよりますが、マイクロサービス アーキテクチャは、マルチアーキテクチャ手法に最適となります。That is why microservices architectures are perfect for a multi-architectural approach depending on each Bounded Context. たとえば、eShopOnContainers の場合、注文マイクロサービスは、DDD パターンを実装しますが、カタログ マイクロサービス (シンプルな CRUD サービス) は、DDD パターンを実装しません。For instance, in eShopOnContainers, the ordering microservice implements DDD patterns, but the catalog microservice, which is a simple CRUD service, does not.

貧血症ドメイン モデルは、アンチパターンであると言われる場合があります。Some people say that the anemic domain model is an anti-pattern. 実際、このモデルは、実装する内容に依存しています。It really depends on what you are implementing. 作成するマイクロサービスが十分にシンプルである場合 (CRUD サービスなどである場合) は、貧血症ドメイン モデルに従い、アンチパターンになりません。If the microservice you are creating is simple enough (for example, a CRUD service), following the anemic domain model it is not an anti-pattern. ただし、絶えず変化するビジネス ルールを多数含むマイクロサービスのドメインの複雑さに取り組む必要がある場合、貧血症ドメイン モデルは、そのマイクロサービスまたは有界コンテキストのアンチ パターンである可能性があります。However, if you need to tackle the complexity of a microservice's domain that has a lot of ever-changing business rules, the anemic domain model might be an anti-pattern for that microservice or Bounded Context. その場合は、データとビヘイビアーを含み、追加の DDD パターン (集約や値オブジェクトなど) を実装するエンティティを持つリッチ モデルとして貧血症ドメイン モデルを設計すると、そのようなマイクロサービスを長期的に成功させるために必要な大きな利点が得られる可能性があります。In that case, designing it as a rich model with entities containing data plus behavior as well as implementing additional DDD patterns (aggregates, value objects, etc.) might have huge benefits for the long-term success of such a microservice.

その他の技術情報Additional resources

値オブジェクト パターンThe Value Object pattern

Eric Evans 氏は、"多くのオブジェクトは概念 ID を持たない。As Eric Evans has noted, "Many objects do not have conceptual identity. これらのオブジェクトは、あるものについての特定の特性を記述している" と述べています。These objects describe certain characteristics of a thing."

エンティティには ID が必要ですが、システム内には値オブジェクト パターンのように ID を持たないオブジェクトが多数あります。An entity requires an identity, but there are many objects in a system that do not, like the Value Object pattern. 値オブジェクトは、ドメインのアスペクトを記述する概念 ID を持たないオブジェクトです。A value object is an object with no conceptual identity that describes a domain aspect. これは、一時的にしか関係しない設計要素を表すためにインスタンス化するオブジェクトです。These are objects that you instantiate to represent design elements that only concern you temporarily. 関心を持つ必要があるのは、それが "" であるのかではなく、"" であるのかという点です。You care about what they are, not who they are. 例には数値と文字列が含まれていますが、属性グループのような上位レベルの概念にすることもできます。Examples include numbers and strings, but can also be higher-level concepts like groups of attributes.

あるマイクロサービスではエンティティであるものが、別のマイクロサービスではエンティティでないことがあります。これは、2 番目のケースで、有界コンテキストが別の意味を持つことがあるからです。Something that is an entity in a microservice might not be an entity in another microservice, because in the second case, the Bounded Context might have a different meaning. たとえば、eコマース アプリケーションのアドレスは、ID を持たない場合があります。これは、個人または会社の顧客プロファイルの属性グループしか表さない場合があるためです。For example, an address in an e-commerce application might not have an identity at all, since it might only represent a group of attributes of the customer's profile for a person or company. この場合、アドレスは、値オブジェクトとして分類する必要があります。In this case, the address should be classified as a value object. ただし、電力会社のアプリケーションでは、顧客のアドレスがビジネス ドメインで重要になります。However, in an application for an electric power utility company, the customer address could be important for the business domain. そのため、請求システムがアドレスに直接リンクできるようにするために、アドレスには ID が必要です。Therefore, the address must have an identity so the billing system can be directly linked to the address. この場合、アドレスは、ドメイン エンティティとして分類する必要があります。In that case, an address should be classified as a domain entity.

通常、個人は ID を持つため、名と姓を持つ個人はエンティティです。これは、その名と姓が別の値セットに一致した場合 (たとえば、それらの名前が別の個人も指している場合) にも当てはまります。A person with a name and surname is usually an entity because a person has identity, even if the name and surname coincide with another set of values, such as if those names also refers to a different person.

値オブジェクトは、リレーショナル データベースや Entity Framework (EF) などの ORM では管理が難しいですが、ドキュメント指向データベースでは簡単に実装して使用できます。Value objects are hard to manage in relational databases and ORMs like Entity Framework (EF), whereas in document-oriented databases they are easier to implement and use.

EF Core 2.0 以降のバージョンには、後で詳しく説明しているように、値オブジェクトを処理しやすくする所有エンティティ機能が含まれます。EF Core 2.0 and later versions include the Owned Entities feature that makes it easier to handle value objects, as we’ll see in detail later on.

その他の技術情報Additional resources

集約パターンThe Aggregate pattern

ドメイン モデルには、機能の重要な領域 (注文調達やインベントリなど) を制御できる、さまざまなデータ エンティティやプロセスのクラスターが含まれています。A domain model contains clusters of different data entities and processes that can control a significant area of functionality, such as order fulfillment or inventory. より細かな DDD 単位は集約であり、まとまりのある単位として処理できるエンティティとビヘイビアーのクラスターまたはグループが記述されています。A more fine-grained DDD unit is the aggregate, which describes a cluster or group of entities and behaviors that can be treated as a cohesive unit.

通常は、必要なトランザクションに基づいて集約を定義します。You usually define an aggregate based on the transactions that you need. 典型的な例は、注文項目のリストも含まれる注文です。A classic example is an order that also contains a list of order items. 通常、注文項目は、エンティティになります。An order item will usually be an entity. ただし、注文集約内では子エンティティとなり、注文エンティティもそのルート エンティティとして含まれます (通常、ルート エンティティは、集約ルートと呼ばれます)。But it will be a child entity within the order aggregate, which will also contain the order entity as its root entity, typically called an aggregate root.

集約を識別することが困難な場合があります。Identifying aggregates can be hard. 集約は、互いに一致する必要があるオブジェクトのグループですが、オブジェクトのグループを選び、集約のラベルをつけることはできません。An aggregate is a group of objects that must be consistent together, but you cannot just pick a group of objects and label them an aggregate. ドメインの概念から開始し、その概念に関連する最も一般的なトランザクションで使用されるエンティティについて考える必要があります。You must start with a domain concept and think about the entities that are used in the most common transactions related to that concept. トランザクションで一致する必要があるエンティティは、集約を形成するエンティティです。Those entities that need to be transactionally consistent are what forms an aggregate. トランザクションの操作について考えることが、おそらく集約を識別するための最良の方法です。Thinking about transaction operations is probably the best way to identify aggregates.

集約ルートまたはルート エンティティ パターンThe Aggregate Root or Root Entity pattern

集約は、集約ルートという 1 つ以上のエンティティから構成されています (集約ルートは、ルート エンティティまたはプライマリ エンティティとも呼ばれます)。An aggregate is composed of at least one entity: the aggregate root, also called root entity or primary entity. また、集約は、複数の子エンティティと値オブジェクトを持つことができ、すべてのエンティティとオブジェクトが連携して、必要なビヘイビアーやトランザクションを実装しています。Additionally, it can have multiple child entities and value objects, with all entities and objects working together to implement required behavior and transactions.

集約ルートの目的は、集約の一貫性を維持することです。集約ルートは、集約ルート クラスのメソッドまたは操作を通じて集約に入るための唯一の更新用のエントリ ポイントである必要があります。The purpose of an aggregate root is to ensure the consistency of the aggregate; it should be the only entry point for updates to the aggregate through methods or operations in the aggregate root class. 集約内のエンティティを変更するときは、必ず集約ルートを経由する必要があります。You should make changes to entities within the aggregate only via the aggregate root. これは、集約の一貫性を管理するもので、集約で従う必要がある可能性があるすべての不変条件および一貫性のルールを考慮しています。It is the aggregate's consistency guardian, considering all the invariants and consistency rules you might need to comply with in your aggregate. 子エンティティまたは値オブジェクトを別々に変更した場合、集約ルートは、集約が有効な状態であることを保証できません。If you change a child entity or value object independently, the aggregate root cannot ensure that the aggregate is in a valid state. 本来の機能を発揮できなくなるでしょう。It would be like a table with a loose leg. 一貫性の管理は、集約のルートの主も重要な目的です。Maintaining consistency is the main purpose of the aggregate root.

図 7-9 には、バイヤー集約などのサンプル集約が示されています。バイヤー集約には、1 つのエンティティ (集約ルート バイヤー) が含まれています。In Figure 7-9, you can see sample aggregates like the buyer aggregate, which contains a single entity (the aggregate root Buyer). 注文集約には、複数のエンティティと値オブジェクトが含まれています。The order aggregate contains multiple entities and a value object.

バイヤー集約と注文集約を比較している図。

図 7-9Figure 7-9. 複数のエンティティまたは 1 つのエンティティを含む集約の例Example of aggregates with multiple or single entities

DDD ドメイン モデルは集約から構成され、集約には 1 つまたは複数のエンティティを含めることができ、値オブジェクトも含めることができます。A DDD domain model is composed from aggregates, an aggregate can have just one entity or more, and can include value objects as well. バイヤー集約は、ドメインによっては、eShopOnContainers 参照アプリケーションの注文マイクロサービスの場合と同様、追加の子エンティティを持つことができることに注意してください。Note that the Buyer aggregate could have additional child entities, depending on your domain, as it does in the ordering microservice in the eShopOnContainers reference application. 図 7-9 は、集約ルートのみを含む集約の例として、バイヤーが 1 つのエンティティを持つケースを示しています。Figure 7-9 just illustrates a case in which the buyer has a single entity, as an example of an aggregate that contains only an aggregate root.

集約を分離し、集約間の境界を明確にし続けるためには、DDD ドメイン モデルで、集約間の直接の移動を禁止し、eShopOnContainers の注文マイクロサービス ドメイン モデルに実装されている外部キー (FK) フィールドのみを持つことをお勧めします。In order to maintain separation of aggregates and keep clear boundaries between them, it is a good practice in a DDD domain model to disallow direct navigation between aggregates and only having the foreign key (FK) field, as implemented in the Ordering microservice domain model in eShopOnContainers. 注文エンティティは、次のコードに示すように、バイヤーの FK フィールドのみを持ち、EF コア ナビゲーション プロパティは持ちません。The Order entity only has a FK field for the buyer, but not an EF Core navigation property, as shown in the following code:

public class Order : Entity, IAggregateRoot
{
    private DateTime _orderDate;
    public Address Address { get; private set; }
    private int? _buyerId; //FK pointing to a different aggregate root
    public OrderStatus OrderStatus { get; private set; }
    private readonly List<OrderItem> _orderItems;
    public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
    // ... Additional code
}

集約を識別し、操作するには、研究と経験が必要です。Identifying and working with aggregates requires research and experience. 詳細については、次の「その他の技術情報」のリストを参照してください。For more information, see the following Additional resources list.

その他の技術情報Additional resources