1

I am writing a service and a library that I need to make framework agnostic because it will be used by some client services. The client services will be written in both Micronaut and Spring frameworks. So I must probably avoid DI in a container.

The library wraps the communication to the service, giving the client services Domain objects to use. So the Domain Context spans between multiple services.

The problem is I cannot use DI in a container. Is it still within Port And Adapters architecture to create and configure adapters implementations in the Application layer (without a container DI) and give them to Domain layer (of course using port interfaces for the Domain layer) ?

Certainly the crucial part in P&A is to use Ports in Domain Layer . But do I need to or should I create another layer just for creating and configuring adapters due to P&A architecture ?


edit: To the down-voters: Could you specify in the comments what is wrong with the question ?

edit2: Edited question title and contents to avoid opinion sounding phrases like "Is it ok".

10
  • Can you give an example where you feel the need to give an adapter to the domain layer? Also clarify what you mean by "using port interfaces for the Domain layer". I have a feeling it has to do with shipping out part of your domain (Logic? Data?) but it's not clear. Lastly, are you asking this question for the "client services" or your own service? Nov 29, 2023 at 8:45
  • I don't mean to give an adapter to the domain directly. That's not the point. I mean something like port interface with save() methods which is implemented by database adapter with JPA persist(). The domain does not depend in any way on the adapter ! Nov 29, 2023 at 20:14
  • ` Lastly, are you asking this question for the "client services" or your own service? ` I and my team write the system which consists with server and clients and I design the server service and library that will be used by the clients to talk to the server. The server and client services are developed by one team there is no need to divide them to separate bounded contexts . Nov 29, 2023 at 20:18
  • 1
    @guillaume31 I've grown up on DDD so that's why Domain in my terms. The programmers who show me DDD with P&A mix still used the term Domain and when I've read years ago the assumptions of Hexagonal I remember (maybe wrong) Application not as layer but as a Application Internals aka Domain. In current doctrine P&A there are Application Layer and Domain Layer. I was surprised by that. Now it is time for me to refresh my memories / maybe I took something wrong. Maybe the projects I saw were not pure DDD or pure P&A. I try to to figure it out during designing new services. Nov 30, 2023 at 21:20
  • 1
    @guillaume31 Don't take me wrong but maybe despite some P&A misunderstanding or maybe misunderstanding of current dominating P&A doctrine I have been in the whole IT business for over 25 years. Nov 30, 2023 at 21:25

1 Answer 1

6

Separate use and construction.

When you say “without DI” I suspect you mean without a DI container. DI existed long before containers were a thing.

DI done with containers has become popular. So popular that Mark Seemann had to make up a word for DI without containers. He calls it Pure DI. But I can assure you, the idea is much older than that. Back in the day we just called it reference passing.

With that cleared up let's get to your question:

Is it ok within Port And Adapters architecture to create and configure adapters implementations in the Application layer

Construction doesn't have to be done in any layer. Construction is what creates the layers. When you're looking at a diagram of ports and adapters you're looking at an object graph. It shows you what knows about what. It doesn't show you what built what. Every long lived thing in that diagram could have been built in main.

Some objects aren't long lived. Some are ephemeral. They must wait until something happens before they should exist (timestamps). These are the ones that must be constructed deep in the call stack. If your adapter fits this description then not only is it ok. It's a must.

But that'd be a fairly weird adapter. Most adapters can be built with all the other long lived objects at the beginning. I mean, unless your adapter is some bloated thing that needs lazy loading.

As for your frameworks, yes, they work very hard at stealing main from you to make you dependent on them. There is a host of tricks to undo that damage. Mark Seemann taught us one called the composition root.

If you can be disciplined about how you use frameworks you can hold them at arms length and keep them from taking over how all objects are constructed. That doesn't mean you can't let them build some. Just that you shouldn't let them bully you into doing everything their way.

Key to that is making sure you have a way to inject things you built yourself into things they built for you. And vice versa. That all comes back to the composition root.

Finding out how to do this isn't easy with most frameworks precisely because they don't want you to be independent of them. Remember, DI is just passing references around. It's not that fancy.

Most DI containers move construction code out of your native language into something else. That helps you separate use and construction. But if you're not careful it also robs you of control over construction.

POJO's can be built without any container annotations by both PureDI and ContainerDI. POJO's with container annotations that can't be constructed by PureDI are all to easy to create. Be aware of what you're loosing before you commit to this style.

But also, be aware that holding a framework at arms length like this comes with a cost. They don't make it easy to stay independent. You have to decide for yourself if the fight is worth it.

Anyway, strictly speaking, since Ports and Adapters doesn't talk about how to construct its object graph there's nothing telling you it's not OK to construct an adapter in the application layer. Any wisdom about that must come from elsewhere.

Separate use and construction

11
  • “without DI” I suspect you mean without a DI container. <= yes you've read it right. Thanks for the answer. To clarify something. The architectural layers are about dependencies. You separate usage from construction. But to construct something there is a place where you must place specific Factory implementation for an adapter. Let's assume I have a PlaceOrderToKafkaAdapter that implements PlaceOrderPort interface . There is some place in the code (probably close to main()) where I construct and configure the adapter to plug it to a domain service OrderDomainService . Nov 29, 2023 at 20:36
  • 1
    @digital_infinity you’re right. The import would indicate a static dependency. There are tricks like passing in an abstract factory that could wiggle around that if you really need to. Nov 29, 2023 at 21:49
  • 1
    The composition root should be as high up the call stack as you can get. Its dependencies can point at everything and nothing should point at it. It should run before any using code is run and, once run, should never run again. Nov 30, 2023 at 0:12
  • 1
    If you don't mind Clean Architecture diagrams, I talk about the pitfalls of breaking up construction by layer here and how to construct immutable objects that support a flexible flow of control here. Should give you a high level view of a pure DI pattern. Nov 30, 2023 at 0:44
  • 1
    @digital_infinity The idea is to leave the process of wiring-up to the peripheral-most client. In a standalone application, this would be the entry point (serves as the CR). And a library may leave it entirely to the end-consumer, providing only some helper methods, or preset configs. With a CR, adapter implementations only depend on the interface they implement, without it, each would have to also depend (or be associated with code that depends) on the domain-layer consumer of that interface. 2/2 Nov 30, 2023 at 17:52

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.