10

If we consider a standard persistence repository, the solution is easy. We put the IStuffRepository in the Domain Layer, and the StuffRepositoryImplementation in the Infrastructure Layer.

But what is the good pattern when we want to wrap a third-party API?

We could apply the same pattern, having an IStuffGateway in the Domain Layer, and a StuffGatewayImplementation in the Infrastructure Layer.

But there is a problem with this approach. When we consider the persistence layer, we have the control about the data we persist. But when we consider the third-party API, we have no control, meaning that we can TRY to have a certain interface signature, but it has to be influenced by what we are wrapping. So if we change the implementation (replacing a third-party by another), interface signature will probably changes, and the domain be altered.

Another approach could be to move the interface outside the domain, and put it in the Infrasture Layer, with it's implementation. This way, the Application Layer is able to use it without problems (and keep the domain intact). But this approach removes important concept from the domain, which seems to be bad from my perspective.

Any opinions of references about this?

Normand Bedard
  • 2,375
  • 2
  • 15
  • 18
  • What do you do when the third-party-persistence is changed? Do you go to stock-holders and explain that their business needs to change? – Constantin Galbenu Jun 06 '17 at 07:55
  • Of course not. Persistence is an implementation detail. But third-party API providers are not always only implementation details. For example, if a business wraps a FedEx API to automate some portion of the shipping, it is something stock-holders are interested in. And if for some business reasons they switch provider, it would be normal to impact the domain in some way. Even with an anticorruption layer, some key concepts related to the third-party API (like confirmation number, SKU, etc.) that could be unique for a specific provider would leak into the domain. – Normand Bedard Jun 07 '17 at 11:17
  • It seems that your domain do really depends on some of the 3rd party API. In this case, I wonder if defining&depending on an `Interface` helps as the details are important (and interfaces remove some of those details). – Constantin Galbenu Jun 07 '17 at 13:35

2 Answers2

6

I always keep my Domain objects (Aggregates) pure, with no side effects. This means that I don't have any dependencies from the Domain to any other layer. The persistence/repository is always in the Infrastructure. The Application layer uses it to persist the Aggregates.

When I use CQRS, I keep only my write/command side (Aggregates) pure. The Readmodels are dependent and optimized to a specific implementation. Latelly I used a lot MongoDB for persistence.

When I don't use CQRS, I keep the whole Aggregate with no dependencies (I have no choice, splitting would be CQRS).

So, all cases, the Domain doesn't do any IO, it has no side effects. This is mainly because I need to be able to safely re-execute a command on the Aggregate in case of concurrent updates.

If however you decide to use an Interface you should use the DIP: the Domain owns the Interface; this means that the domain decides the number and signature of methods.

Constantin Galbenu
  • 14,628
  • 3
  • 23
  • 41
  • 1
    I think I did not express myself correctly because I never wanted to imply that my domain as dependencies to other layer. What I say, is my repository interface (IStuffRepository is declared in my domain layer. The implementation (StuffRepository) is in the Infrastructure Layer. The problem is about the location of the Interface wrapping my access to external API. – Normand Bedard May 31 '17 at 13:14
  • You did express yourself correctly. That is what I'm saying. External persistence should not influence your domain models. Your Repository interface should have the same signature (load, save, delete) when the external persistence changes. The location of that interface should stay inside the Domain layer. – Constantin Galbenu May 31 '17 at 13:42
  • 2
    This is true for data Repositories. But my question is not related to persistance repositories. This is not the case for interfaces wrapping access to third-party APIs. Imagine a payment gateway where you need to provide a clientId as a Guid. This third-party concern directly leaks into your domain. And the day you change the payment provider, maybe you will need a clientId as a Integer. That is what confuses me because the Domain Layer is supposed to be the appropriate place to put the IPaymentGateway interface since it represents a use case of the bounded context. – Normand Bedard May 31 '17 at 14:46
  • Then you should implement an Anti Corruption Layer – Constantin Galbenu May 31 '17 at 14:59
  • 1
    I'm not sure why this is the accepted answer if the real answer (Anti Corruption Layer) is way down in the comments? – Steven Liekens Apr 05 '19 at 12:27
2

I am in agreement with Constantin here.

You can still keep the concept in your domain but not the IO. The application layer should provide the required domain objects. You could call your anti-corruption layer (IStuffGateway) and get the required object(s) that you then pass in through some call to your domain.

If this is a common task you may want to introduce an application service (IGetStuffAndCallDomainTask) that wraps that bit.

Eben Roux
  • 12,009
  • 2
  • 23
  • 43
  • My question is related to the location of the interface wrapping a third party API. Typically repository interfaces are located in the domain, and the implementations are in the infrastructure layer. But when it comes to interface wrapping an external API, there are some drawbacks to apply the same pattern because the lack of control of the signature. – Normand Bedard May 31 '17 at 13:28
  • Since it isn't really a domain concern the interface should not be defined there, no. Perhaps your ACL would be a more appropriate location. – Eben Roux May 31 '17 at 13:58
  • 1
    So you are saying that my domain should not be aware at all about some interface signature of wrapper around external API? This means the Application Layer would be the only place to be aware of that (through the anti-corruption layer in the Infrastructure Layer) when orchestrating a full business use case... This would definitly works, but I still have the feeling the domain should be aware of something. But it is true that calling an external API typically involves no business logic at all, and is part of a business use case in the app layer. – Normand Bedard May 31 '17 at 14:58
  • Your domain would probably have the *local* definition/object that you are after. For instance, say you are calling some address gateway that returns a valid address. You may very well still have an `Address` in *your* domain that you rely on. The application layer would do the call and get back, say, the mapped `Address`: `myGatewayImplemenation.Fetch("streetName", streetNumber)`... internally it would map from, say, the returned `addrObj` from the web-service call. – Eben Roux May 31 '17 at 18:09