0

I'm building a DDD application which is effectively a wrapper around Cognito. As such, everything that this is doing is effectively just a call onto the Cognito SDK.

Where I'm getting confused is how this should work with respect to Domain Services or Repositories.

I've got an Aggregate to represent a User. I then have a number of actions that I want to be able to support:

  • Load a user by ID
  • Update a user
  • Delete a user
  • Send an email confirmation message
  • Verify the users email address
  • Configure MFA
  • Verify an MFA code
  • Change the users password

Now, the first three of these feel like they fit a Repository well. They're basically Retrieve, Update and Delete from the standard CRUD model.

However, the other five I'm less sure about. They are actions performed on the user, but they're not standard CRUD ones. However, all eight of these are done using the same SDK instance.

So, would it be better to:

  • Put all of these in a Repository?
  • Put all of these - even the CRUD ones - in Domain Services?
  • Put some of these on a Repository and others on Domain Services? (Even though they're all backed by the same SDK instance)
  • Put some of these on a Repository and others on the Aggregate? (Though this would then mean that the Aggregate has calls that affect a remote service, which seems bad)
  • Something else entirely?

To add to this, the "Verify an MFA Code" action is needed as a step in some other actions - namely "Delete a user" and "Change the users password". Which definitely makes it feel like a Domain Service.

Cheers

3
  • Hard to speak about DDD without introducing what the domain is about. I mean, authentication and authorization are usually business and application concerns, so the first that comes to mind is why is the OP struggling with something that is not supposed to happen? On the other hand, are you sure that all these actions must be visible by the domain? Is not a service supposed to abstract the consumer from AWS' SDK details? After all, services are facades. Aren't they?
    – Laiv
    Commented May 17, 2023 at 7:40
  • In this case, the domain is "User Management". So it's actually offering APIs by which user records can be retrieved and updated, where those updates can include simple CRUD updates to the user profile, but also things like verifying the email address, setting up MFA and so on. And yes, the idea is to abstract away the AWS SDK - it could be entirely replaced by e.g. Auth0 and the majority of the code doesn't need to change at all.
    – Graham
    Commented May 18, 2023 at 8:05
  • Then #2 and #3 are both good. The only reason to go with #3 is to keep, somehow, DDD semantics, but it's not especially better than #2. Unless you think that CRUD Ops and Auth Ops will be backed by different services someday. If that ever happens, you will find #3 to be more convenient. But it's an exercise of interface segregation. The key here are not the concrete classes, the abstractions are.
    – Laiv
    Commented May 18, 2023 at 8:23

3 Answers 3

0

In Domain-Driven Design, Repositories are responsible for persisting and retrieving Aggregates, while Domain Services are responsible for encapsulating business logic that does not naturally fit within the Aggregate's boundaries. Given that all of the actions you've listed involve interacting with a remote service, it may make sense to create a CognitoService or similar class to handle these operations. This could be injected into your User Repository as a dependency, allowing you to keep your CRUD operations separate from your non-standard actions.

Regarding the "Verify an MFA Code" action, it seems like this would be better placed in the CognitoService rather than the Domain Service or the Aggregate, as it is closely tied to the Cognito SDK.

In summary, you could create a User Repository that handles the standard CRUD operations and relies on a CognitoService to handle the non-standard actions that interact with the Cognito SDK. This would allow you to keep your concerns separate and adhere to the Single Responsibility Principle.

0

From the perspective of your domain/business logic, how is a remote API different from a database server? We could arguably cast this net even wider and ask the same question for any kind of input/output that is not generated by your domain/business logic itself.

Other than the concrete implementation to communicate with it, there is no difference. It functions the same way. In either case, you're dealing with a resource that is not under your control, and you are firing requests at it and parsing its response (if any response is given).

Whether you call it a repository or a service is a nomenclature distinction, not a technical one. That being said, this is not a definitive argument for lumping them together either. I'm just pointing out that the decision here is a subjective one based on what makes intuitive sense to us. On a technical level, it's all the same.

I would consider that the concrete implementation is best kept together, since it's using the same remote resource and there's some shared (private) logic that goes along with it.

However, an argument can be made to separate the interface between a repository (aggregate data storage) and service (logical operations).

Based on the question as asked, I think a single class which implements two distinct interfaces makes the most sense.

0

A repository, for instance, is an object with methods for retrieving domain objects from a data store (e.g. a database).

According to wikipedia Domain driven design page.

Between the lines everything that can take part in a transaction, or differently said everything that could be rolled back, should be placed in repository, everything else should be placed in service to make the difference clear and follow the principle of least astonishment1 when working with transactions.

1 a component of a system should behave in a way that most users will expect it to behave, and therefore not astonish or surprise users. Principle of least astonishment according to wikipedia.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.