2

I'm playing around with DDD, and in particular in cases that involve remote service calls. I'm building a system where the user records are actually backed by Cognito, so I've got to work with the Cognito IdentityProvider SDK instead of a local database.

In terms of just loading and saving data, that's pretty easy. I've just got a Repository concept that is itself backed by the IdentityProvider SDK, so from the outside it works the same as if it was a local database.

Where I'm getting conflicted is how best to architect the other parts of the system. For example, changing the users password.

The flow for this is:

  • Load the user details.
    • This involves a call to the AdminGetUser action on the IdentityProvider SDK.
  • Check if the user has MFA enabled.
    • If so, verify the provided MFA token in the API request.
    • This is a call to the AdminRespondToAuthChallenge action on the IdentityProvider SDK.
  • Actually change the password.
    • This is a call to the ChangePassword action on the IdentityProvider SDK.

It seems that there are at least three ways that this can be structured:

  1. The User Entity itself is able to make remote calls.
  2. The Application Service makes remote calls.
  3. There are Domain Services that make remote calls, and the Application Service uses these.

I've asked elsewhere about #1 and have been recommended that the User Entity shouldn't be doing anything itself that directly impacts other systems.

#3 feels slightly cleaner - it means that there are domain concepts within the application for these things, and the making of remote calls is hidden behind those. However, it feels like this is just a lot of extra layers to achieve this. In particular, it ends up something like this: UML Diagram

So we have three different components that each have the SDK Client in them, all three of which are then in the same Application Service.

Is this the recommended way? Is there a better recommended way to structure this? Am I just massively over-thinking things? :)

Cheers

1 Answer 1

1

I strongly prefer #3, having done this a few times, although lots of the times that's really an infrastructure service.

Advantages:

  • Unless your domain concept of a User is identical (and will always be so) to the 3rd party service's definition, you need some sort of adapter between the two in both directions. Having this in a well-scoped service makes it nicely isolated from the rest and easy to change as the external service does.
  • There's often a lot of logic around "ok, what do we do if it fails" or other such concerns that the application service shouldn't have to care about.
  • There's often a fair amount of boilerplate to format the requests. Stuff that should happen once per external service. Stuffing that in your application service just bloats it, especially when you have a number of external services.
  • You can nest multiple calls in one where appropriate. If (in your stated case) you're always loading the details and then doing MFA, you can have that just be one call to the service.

Downsides:

  • It's one more class per external service. Meh. Not a big deal IMO.

As far as the application service and the domain itself are concerned, that 3rd party is just magic. You ask it for stuff and it hands you exactly what you want in the format you want it in. Which is what a separate service provides--a facade over the more complex SDK.

2
  • I guess in this exact case it's slightly confused by the fact that AWS provide an SDK layer that you need to use. So it ends up being Application Service calling Domain Service calling SDK, and the SDK itself handles all the complexity of dealing with the API. But I fully see the benefit in that if it was direct HTTP calls or similar, and following the same pattern is probably prudent to reduce complexity if it did change to something like that :)
    – Graham
    Mar 6, 2023 at 19:18
  • 1
    It's the adapter part that's critical, and unless you're using their types you're going to need one. We've abstracted away the SDKs (including AWS ones) in a service for just that reason--we don't want to have to deal with conformance to all their types in the main code. Mar 6, 2023 at 21:35

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.