I have an onion architecture that follows this diagram:
In which each project is connected to the "center" through interfaces.
Now, I am in the situation in which the Payment Gateway needs to store some data. It would be amazing if it had access to the DB, but I realize I cannot connect them directly, this would break the "onion"structure! (and the IoC principle!)
So, it seems that the solution is to create an interface that I would implement in persistence and that I would pass into PaymentGateway. This would allow it to save its own things. BUT this interface might have to be very specific (i.e. void SavePaymentGatewayCustomer(Customer customer);
or void SavePaymentGatewayCustomerPaymentMethod(PaymentMethod paymentMethod);
and to me this also breaks IoC as too much information about the PaymentGateway is known by both Persistence and the "center".
As it seems a problem that everyone will encounter sooner or later, I wonder how you solved the issue.
Additional Info:
The structure of the solutions is:
The interfaces are defined in the "AbstractionLayer" project and look like:
public interface IUsersPersistenceHandler : IDisposable
{
/// <summary> Gets the user. </summary>
/// <param name="id">The user identifier.</param>
Result<IUser> GetUser(string id);
...
}
These interfaces are then implemented by the peripheral projects:
public sealed class VolatileUserPersistenceHandler : IUsersPersistenceHandler
{
private readonly Dictionary<string, Individual> _individuals;
private readonly Dictionary<string, Organization> _organizations;
/// <inheritdoc />
public Result<IUser> GetUser(string id) { ... }
...
}
Through Dependency Injection, the implementations are injected in the API level, that in this way doesn't know anything about the outer projects and only knows the interfaces (in this way, a database persistence can easily be swapped with a volatile persistence. Or the payment gateway can be substituted with another one without having to rewrite everything).
This is the simple situation. The problems occur when PaymentGateway needs to be able to store/retrieve information of its own.
Let's say that PaymentGateway has its own user definition. Now we want to associate OUR user (in CORE) with the Gateway's and we want to save the information somewhere.
For example, the user in the PaymentGateway has a string GatewayId
and string CustomerId
that we need to store somewhere.
Remember, this is the situation: CORE doesn't want to know anything about details implementation! It only knows what the interfaces know.
The onion architecture describes an application. The systems outside the onion are other applications.
The payment gateway may have a persistence need, but this should be unrelated to the persistence engine that the application in question uses.
If, somehow, the application that you architect with the onion architecture needs to persist some data from the payment gateway in its persistence system, it's its responsibility to do so. In that case, then, there has to be some application code in Core (or the 'controller' layer just in the next layer out) that handles that.
Read more about the Onion Architecture and Dependency Injection here.