避免洋葱架构中的跨项目依赖
Avoid cross project dependencies in onion architecture
我有一个遵循此图的洋葱架构:
其中每个项目都通过接口连接到 "center"。
现在,我处于支付网关需要存储一些数据的情况。如果它可以访问数据库,那就太棒了,但我意识到我不能直接连接它们,这会破坏 "onion" 结构! (以及 IoC 原则!)
因此,似乎解决方案是创建一个接口,我将在持久性中实现该接口并将其传递到 PaymentGateway。这将允许它保存自己的东西。但是这个接口可能必须非常具体(即 void SavePaymentGatewayCustomer(Customer customer);
或 void SavePaymentGatewayCustomerPaymentMethod(PaymentMethod paymentMethod);
,对我来说这也破坏了 IoC,因为 Persistence 和 "center" 都知道关于 PaymentGateway 的太多信息。
好像每个人迟早都会遇到的问题,不知道你是怎么解决的。
附加信息:
解决方案的结构是:
接口在 "AbstractionLayer" 项目中定义,看起来像:
public interface IUsersPersistenceHandler : IDisposable
{
/// <summary> Gets the user. </summary>
/// <param name="id">The user identifier.</param>
Result<IUser> GetUser(string id);
...
}
这些接口随后由外围项目实现:
public sealed class VolatileUserPersistenceHandler : IUsersPersistenceHandler
{
private readonly Dictionary<string, Individual> _individuals;
private readonly Dictionary<string, Organization> _organizations;
/// <inheritdoc />
public Result<IUser> GetUser(string id) { ... }
...
}
通过Dependency Injection,将实现注入到API级别,这样就不知道任何外部项目,只知道接口(这样,数据库持久化很容易用不稳定的持久性交换。或者支付网关可以用另一个替换,而不必重写所有内容)。
这是简单的情况。当 PaymentGateway 需要能够 store/retrieve 自己的信息时,就会出现问题。
假设 PaymentGateway 有自己的用户定义。现在我们想将我们的用户(在 CORE 中)与网关的用户相关联,我们想将信息保存在某个地方。
例如,PaymentGateway 中的用户有一个 string GatewayId
和 string CustomerId
我们需要存储在某个地方。
记住,情况是这样的:CORE 不想知道任何细节实现!它只知道接口知道什么。
洋葱架构描述了一个应用程序。洋葱之外的系统是其他个应用程序。
- 数据库在另一个进程中运行,通常在不同的机器上
- 邮件系统在另一个进程中运行,通常在不同的机器上
- 支付网关(我假设)在另一个进程中运行,通常在互联网上的其他地方
- 等等...
支付网关可能有持久性需求,但这应该与相关应用程序使用的持久性引擎无关。
如果您使用洋葱架构构建的应用程序以某种方式需要在 its 持久性系统中保存来自支付网关的一些数据,则 its这样做的责任。那么,在那种情况下,Core(或下一层的 'controller' 层)中必须有一些应用程序代码来处理该问题。
阅读更多关于洋葱架构和依赖注入的信息here。
我有一个遵循此图的洋葱架构:
其中每个项目都通过接口连接到 "center"。
现在,我处于支付网关需要存储一些数据的情况。如果它可以访问数据库,那就太棒了,但我意识到我不能直接连接它们,这会破坏 "onion" 结构! (以及 IoC 原则!)
因此,似乎解决方案是创建一个接口,我将在持久性中实现该接口并将其传递到 PaymentGateway。这将允许它保存自己的东西。但是这个接口可能必须非常具体(即 void SavePaymentGatewayCustomer(Customer customer);
或 void SavePaymentGatewayCustomerPaymentMethod(PaymentMethod paymentMethod);
,对我来说这也破坏了 IoC,因为 Persistence 和 "center" 都知道关于 PaymentGateway 的太多信息。
好像每个人迟早都会遇到的问题,不知道你是怎么解决的。
附加信息:
解决方案的结构是:
接口在 "AbstractionLayer" 项目中定义,看起来像:
public interface IUsersPersistenceHandler : IDisposable
{
/// <summary> Gets the user. </summary>
/// <param name="id">The user identifier.</param>
Result<IUser> GetUser(string id);
...
}
这些接口随后由外围项目实现:
public sealed class VolatileUserPersistenceHandler : IUsersPersistenceHandler
{
private readonly Dictionary<string, Individual> _individuals;
private readonly Dictionary<string, Organization> _organizations;
/// <inheritdoc />
public Result<IUser> GetUser(string id) { ... }
...
}
通过Dependency Injection,将实现注入到API级别,这样就不知道任何外部项目,只知道接口(这样,数据库持久化很容易用不稳定的持久性交换。或者支付网关可以用另一个替换,而不必重写所有内容)。
这是简单的情况。当 PaymentGateway 需要能够 store/retrieve 自己的信息时,就会出现问题。
假设 PaymentGateway 有自己的用户定义。现在我们想将我们的用户(在 CORE 中)与网关的用户相关联,我们想将信息保存在某个地方。
例如,PaymentGateway 中的用户有一个 string GatewayId
和 string CustomerId
我们需要存储在某个地方。
记住,情况是这样的:CORE 不想知道任何细节实现!它只知道接口知道什么。
洋葱架构描述了一个应用程序。洋葱之外的系统是其他个应用程序。
- 数据库在另一个进程中运行,通常在不同的机器上
- 邮件系统在另一个进程中运行,通常在不同的机器上
- 支付网关(我假设)在另一个进程中运行,通常在互联网上的其他地方
- 等等...
支付网关可能有持久性需求,但这应该与相关应用程序使用的持久性引擎无关。
如果您使用洋葱架构构建的应用程序以某种方式需要在 its 持久性系统中保存来自支付网关的一些数据,则 its这样做的责任。那么,在那种情况下,Core(或下一层的 'controller' 层)中必须有一些应用程序代码来处理该问题。
阅读更多关于洋葱架构和依赖注入的信息here。