Search code examples
c#domain-driven-designddd-service

How to model this domain driven design challenge?


Learning DDD here.

There is two kind of users Members and Staff. A Member can have a list of subscriptions and are able to purchase additional subscriptions at any time. Staff user can also add a subscription to a member. When staff user adds a subscription to the member system should remember who added this subscription.

Members and staff users are in completely different bounded context. They have the different set of rights and the different group they can attend. So I created a Member aggregate root which contains a list of Subscriptions in separate bounded context "Member". Then i created Staff aggregate root in seprate Bounded context Staff".

When members want to purchase additional subscription it is easy, MemberService just attaches a new Subscription to a member because Subscription is part of the Member aggregate and Member bounded context.

However, when staff user wants to add a new subscription to a user the problem starts because it's not the member who purchase a subscription to itself anymore, it is staff who assign a subscription to a member.

I see multiple solutions here, but none of them seems completely right.

  1. Add Subscription entity to Staff aggregate. (awkward, because staff don't have subscriptions)
  2. When staff user adds a subscription, raise an event which adds member subscription onto the member through member aggregate. (awkward because it's not member who adds the subscription)
  3. Call member aggregate directly from staff service (violates DDD principles).
  4. Move Subscription in a bounded context for itself and make it an aggregate root. (would be wrong because member have a tight relation to his/her subscriptions)

What am I missing here?

Sub question:

Is it allowed to use same DTOs across multiple bounded contexts services?


Solution

  • From what I understand you have indeed at least two bounded contexts but not (only?) those that you identified: Subscriptions bounded context and Authorization bounded context.

    Subscriptions bounded context contains the logic that is used when the members get new subscriptions or cancel an existing subscription. The rules are like this: a member can have as many subscriptions he wants. A subscription can be added by somebody else.

    Authorization bounded context contains the rules by which a user may add subscriptions to a member. If the user is a member then he may add subscriptions only to his member account. If the user is from the staff then he may add subscriptions to other members but not to its own, unless he is also a member (the first rule).

    Having two bounded contexts, you need to integrate them. In the use case "adding a subscription to a member" there are used both bounded contexts. You can implement this in the Application service by calling first a query/domain service from the Authorization bounded context to check if the current authenticated user may add a subscription to the member. If he may not then return with an exception otherwise load the MemberSubscriptions aggregate, call addSubscription(subscriptionId, idIfTheUserThatAddsTheSubscription, subscriptionType, etc...) then persist the aggregate in the repository.

    Note that the Application service did not instantiated a new Subscription entity, that is the job of the MemberSubscriptions aggregate and you must protect its encapsulation. You can even keep the Subscription entity class as private (protected, package or whatever language mechanism you have in C#). Anyway, any access to a members subscription must be done from the MemberSubscriptions aggregate root (like addSubscription, removeSubscription, hasSubscription etc).