I'm building an application for work using a hexagonal architecture and I have an entity (in the core layer) called CompanySetup. When my system receives a request I call my business service which calls a CompanySetupRepository (interface defined in the core layer). In the adapters layers, I've defined the CompanySetupRepository which calls the setup microservice to get the setup and returns to the caller (it's an external data repository). My question is that the setup microservice requires authentication to generate a token, I've decided to put the authentication in every method of my repository implementation (adapters layer). But, since I'm using inversion of control, in which layer should I put the SetupAuthentication - interface - in the core or in the adapters layer?
The same problem I face with encryption code, where should It live? in the Core or adapter layer?
There is nothing as a "core layer" in hexagonal architecture. HA defines 3 layers in the application :
CompanySetup
ICompanySetupRepository
CompanySetupRepository
However, the important part is that adapters is not to be considered an homogeneous layer of your application. Each port defines a "scope" that adapters implement. An adapter can implement one or multiple ports, but it should not directly reference code from another adapter. If it needs an external service, it should reference the adapter's port, letting the dependency injection library do the job.
Rephrasing your question, where should the ICompanySetupAuthentication
and ICompanySetupEncryption
interfaces go ? There are two possible answers for this question.
If the implementation of these two interfaces are specific to the CompanySetup adapter, and should not be referenced by other adapters, then you can place them inside the CompanySetup adapter, with an internal/package scope. You can use the DI inside your adapter in order to split your adapter code into smaller, more maintainable code.
If you want to reuse the code with other adapters, then place the interfaces in the ports layer, and have each interface realization be an adapter. Adapters cannot reference code form each other, so the CompanySetupRepository
will have to reference the ICompanySetupAuthentication
port. The DI will do the rest at runtime.
You could also split the feature in two, having a generic "encryption" port/adapter and a "company setup" port/adapter that includes specific encryption code. The specific code would determine which properties of the object are to be ciphered/deciphered, deferring actual encryption to the other adapter via its port. This way you can change the ciphering algorithm for your whole application (from RSA to AES for instance) without having to fully rewrite how to cipher/decipher a CompanySetup
.