2

Should our api-response-to-domain-entity transform functions be in the API/Infrastructure layer, or the Domain layer?

We have a react-native mobile application written in TypeScript. We have separated "layers" to, amongst other reasons, reduce dependency on outer layers by inner layers, inspired by various well-known software architectures e.g. "Onion". It does not comply exactly with above-mentioned architectures, but dependency between layers looks somewhat like this:

Infrastructure/API <- Services <- Domain/Entities -> Presenters -> UI Components

We are moving everything except UI to a shared library in a monorepo package, to share logic between the react-native application and a react web application. The UI component implementations are too different to be worth sharing.

In our domain layer, we have transformer functions that consume the response from the API/Infrastructure layer that satisfies an interface definition of what we expect from the API endpoint, and return a value that satisfies the interface definition of our Domain Entity. The interface definition for the server API endpoint response is part of the API/Infrastructure layer's concern, so it is located there (infrastructure/). The interface definition for the Domain Entity is located in the Domain layer (domain/). I want to move the Domain layer modules and types to the shared library first, but the transform functions have to import the interface definitions from the API/Infrastructure layer. This feels like a tight coupling.

Should our api-response-to-domain-entity transform functions be in the API/Infrastructure layer, or the Domain layer?

2
  • can you clarify the nature of the app. Is the API is your API that the UI calls to get data OR an external API your consume?
    – Ewan
    Commented Oct 28 at 17:19
  • 1
    On the face of it this is an easy answer : No the API -> Domain mapper shouldn't be in the domain. It is an infrastructure thing. But I don't see how that helps you
    – Ewan
    Commented Oct 28 at 17:26

1 Answer 1

4

The dependency graph you created had me a little confused at first. If the domain layer points to the service layer, to me that means "the domain layer depends on the service layer," but I don't think that's what you really mean. For my answer, I will assume you mean:

Infrastructure/API -> Services -> Domain/Entities <- Presenters <- UI Components

The arrow should point from the dependent to the dependency, so Services -> Domain/Entities means "the Service layer depends on the Domain/Entities."


First:

We have separated "layers" to, amongst other reasons, reduce dependency on outer layers by inner layers

Later on you describe your dependencies as:

Infrastructure/API -> Services -> Domain/Entities <- Presenters <- UI Components
                               ^^^^^^^^^^^^^^^^^^^^^

However, you also say that the domain layer depends on these API contracts in the infrastructure layer. Your dependency graph is actually:

Infrastructure/API -> Services -> Domain/Entities <- Presenters <- UI Components
        ^                               \ /
       / \                               |
        |                                |  <-- TODO: fix this
        |                                |
        +--------------------------------+

Notice the big honk'n arrow from the domain layer back to the infrastructure layer defining that dependency your inner layer has on the outer most layer. Obviously, this is the heart of your question, and the problem to solve.

The first step to solving this is to define exactly what will be reused, and what will not. Given that the UI components would not be reusable, I would expect the presenters and UI components to be application-specific with everything else being reused.

Infrastructure/API -> Services -> Domain/Entities <- Presenters <- UI Components
|                                               |    |                         |
+-----------------------------------------------+    +-------------------------|
                (shared library)                       (application specific)

Eliminating the application specific bits leaves us with:

Infrastructure/API -> Services -> Domain/Entities

Which is much easier to track in our heads. So my first question is, what does this "Services" layer do beyond make API calls? If it only seems like a pass-through to the infrastructure/API layer, then put this mapping logic in the services. Keep your infrastructure from knowing too much about the application.

Offhand, that's what I would do first: put the mapping/transformation logic inside the services.

Once that becomes too unwieldy, consider spinning the transformation logic off into its own object, which becomes another dependency of the Services layer.

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.