0

Say I have a simple layered .NET application, with three layers:

  • API
  • Adapters
  • Application

simplified architecture diagram

The application layer needs to use some typed http clients to communicate with external APIs. This means that the typed http clients cannot be put in the Infrastructure layer, unless interfaces are provided in the application (similar to this example).

This is where I am starting to get confused. Because if I define the interfaces in the Application layer, then the models returned by the clients also needs to be defined in the Application layer. This means that the clients not only needs to communicate with the API, but also needs to map to the objects defined in the application layer (of course you could make those objects match the API contract, but that would create an implicit dependency from the Application layer to the API).

Are typed clients acutally more intended as a sort of service/adapter, which handles more than just API communication? Or am I missing something fundamental here?

Update as per Filip's comment:

It seems that the only way to solve the dilemma is to either make an Adapter layer which can sit on top of the typed http clients (or the clients could build as adapters directly) or to make the application depend directly on the clients (which for smaller applications may be an okay solution).

Updated diagram

3
  • 1
    Ideally, the models (data structures or proper objects) returned by the application layer interface, and the methods on the interface, would not be defined by the external API specification. Otherwise you indeed have an implicit dependency on the API contract. Instead, they would be expressed in terms of what the application layer itself needs - in terms of its own business logic, and at the appropriate level of abstraction. That's what allows for decoupling, not the mere presence of the interface. However, sometimes it's quicker/simpler to just depend on the API. Commented Aug 15, 2023 at 14:40
  • @FilipMilovanović I think I get what you're saying. I updated my question with a new section, which I think describes what you are suggesting...? Commented Aug 15, 2023 at 16:41
  • 1
    I think we're roughly on the same page. I've expanded my comment into an answer, hopefully that'll provide some more clarity. Commented Aug 15, 2023 at 19:02

1 Answer 1

1

Ideally, the models (data structures or proper objects) exposed by the application layer interface, as well as the methods on the interface, would not be defined by the external API specification. Otherwise you indeed have an implicit dependency on the API contract.

Instead, they would be expressed in terms of what the application layer itself needs - in terms of the business logic, and at the appropriate level of abstraction. That's what allows for decoupling, not the mere presence of the interface.

It's hard to explain without a concrete example, but if you work in terms of the data structures of the external API directly, you'll typically pull them in, do some intricate fiddling with them in order to accomplish some goal, or do some calculation at the business logic–level, and then maybe store something to the DB, or whatever. Maybe there's a comment at the top of this block of code explaining what the code is for, what it's trying to do.

A decoupled design would instead take that business-level goal you want to accomplish (or aspects of it), and represent it directly somehow. E.g. you could have an interface (or some other kind of abstraction) with methods that quite literally describe the business-level operations. Method names take the role of those explanatory comments I mentioned. Then the code doesn't read as "fetch this or that, then do a bunch of things, then do some final step", but rather "do this business-level thing, do that business-level thing". Almost like reading a bullet list of high-level requirements.

Such an interface belongs to the application layer (it's "owned" by its client), but its implementation would most likely go into the infrastructure layer - and so you have dependency inversion between the client of that interface, and the implementation of that interface. And you're right, the implementation here is a kind of an adapter (not necessarily a separate top-level layer, though).

Depending on how you designed this, it may or may not involve extensive mapping. E.g. you might not need to map all the data you get from the API, and/or you might return (or pass) objects that have behavior, and that encapsulate parts of the data - objects that you can ask to do things for you.

However, doing design of this sort is not easy and it does take some effort, and some trial and error. On top of that, it may be seen as "nonstandard" in companies that are used to following some "canned architecture" approach, so you may encounter resistance. And of course, sometimes the separation is not really that important, and it's quicker/simpler to just depend on the API. Especially if the API is stable, and you don't expect to switch to something else.

One final point: Microsoft and other software vendors (I'm not singling out Microsoft here) don't necessarily have the same goals as you when it comes to design. They are more concerned with making their frameworks and libraries relatively easy to use, to help a broad range of developers get something up and running quickly - and so they might provide examples that show you how to do that (I'm speaking very generally, and not specifically about the typed HttpClient article, as I didn't read it in depth). This sometimes might clash to a lesser or a greater extent with your design goals.

Basing your design/architecture on those examples does not necessarily result in a good long-term architecture, so don't take everything that Microsoft or other companies put out there as a "best practice", or as an indication of what the design of your specific project should look like.

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.