【发布时间】:2020-03-14 08:09:15
【问题描述】:
注意:以下是一个类似的问题——How to avoid Cyclic Dependencies when using Dependency Injection?——但并没有完全解决我的情况。
我正在尝试开发应用程序架构。我目前已经确定了对三个不同层的需求:API、业务和数据访问。我的目标是基于这里的一个松散耦合的依赖注入设计:https://www.forevolve.com/en/articles/2017/08/11/design-patterns-web-api-service-and-repository-part-1/#the-patterns。
总而言之,NinjaController (API) 包含 INinjaService (BLL),由 NinjaService (也是 BLL) 实现,其中包含 INinjaRepository (DAL),由 NinjaRepository (也是 DAL) 实现)。
由于我打算使用依赖注入,所以还有一个组合根,它必须依赖于所有上述 5 个定义,以便构建依赖图。到目前为止,一切都说得通。
当我开始将事物拆分为不同的程序集时,我遇到了麻烦。我目前的理解(或缺乏)如下:
- Assembly 0 包含 API 实现以及 BLL 接口,用于实现可互换的 BLL。
Assembly 1 包含 BLL 实现以及 DAL 接口;因此,Assembly 1 的 DAL 接口依赖于 Assembly 0。
最后,Assembly 2 包含 DAL 实现,它依赖于 Assembly 1 的 BLL 接口。
但是,Assembly 0 还包含组合根,它依赖于 BLL 和 DAL 接口,以及 API、BLL 和 DAL 实现。
所以Assembly 0和Assembly 1之间存在循环项目依赖,其中0中的root依赖于1中的BLL实现,而1中的BLL实现依赖于0中的BLL接口。
到目前为止,我能做的最好的事情就是让 BLL 接口也驻留在程序集 1 中,但这似乎违背了接口的全部目的。
那么请有人指出我的误解在哪里,如果可能的话,如何实现这一点 设计?
编辑
首先,我可能应该通过不止一个标签来阐明我的设置 - 我使用的是 ASP.NET Web API 应用程序层(不是 .NET Core)。
其次,为了进一步说明我的预期设置,如下所示(再次基于上述来自 forevolve.com 的示例):
// API
namespace ForEvolve.Blog.Samples.NinjaApi.Controllers
{
[Route("v1/[controller]")]
public class NinjaController : Controller
{
private readonly INinjaService _ninjaService;
public NinjaController(INinjaService ninjaService)
{
_ninjaService = ninjaService ?? throw new ArgumentNullException(nameof(ninjaService));
}
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<Ninja>), StatusCodes.Status200OK)]
public Task<IActionResult> ReadAllAsync()
{
throw new NotImplementedException();
}
...
}
}
// BLL Interface
namespace ForEvolve.Blog.Samples.NinjaApi.Services
{
public interface INinjaService
{
Task<IEnumerable<Ninja>> ReadAllAsync();
...
}
}
程序集 1(参见 https://www.forevolve.com/en/articles/2017/08/30/design-patterns-web-api-service-and-repository-part-6/ 和 https://www.forevolve.com/en/articles/2017/09/04/design-patterns-web-api-service-and-repository-part-7/)
// BLL Implementation
namespace ForEvolve.Blog.Samples.NinjaApi.Services
{
public class NinjaService : INinjaService
{
private readonly INinjaRepository _ninjaRepository;
private readonly IClanService _clanService;
public NinjaService(INinjaRepository ninjaRepository, IClanService clanService)
{
_ninjaRepository = ninjaRepository ?? throw new ArgumentNullException(nameof(ninjaRepository));
...
}
...
public Task<IEnumerable<Ninja>> ReadAllAsync()
{
throw new NotImplementedException();
}
...
}
}
// DAL Interface
namespace ForEvolve.Blog.Samples.NinjaApi.Repositories
{
public interface INinjaRepository
{
Task<IEnumerable<Ninja>> ReadAllAsync();
...
}
}
// DAL implementation
namespace ForEvolve.Blog.Samples.NinjaApi.Repositories
{
public class NinjaRepository : INinjaRepository
{
private readonly INinjaMappingService _ninjaMappingService;
private readonly ITableStorageRepository<NinjaEntity> _ninjaEntityTableStorageRepository;
public NinjaRepository(INinjaMappingService ninjaMappingService, ITableStorageRepository<NinjaEntity> ninjaEntityTableStorageRepository)
{
_ninjaMappingService = ninjaMappingService ?? throw new ArgumentNullException(nameof(ninjaMappingService));
_ninjaEntityTableStorageRepository = ninjaEntityTableStorageRepository ?? throw new ArgumentNullException(nameof(ninjaEntityTableStorageRepository));
}
...
public Task<IEnumerable<Ninja>> ReadAllAsync()
{
throw new NotImplementedException();
}
...
}
}
不幸的是,这个示例项目使用的是 .NET Core,此时我只是试图掌握在多层 Web 应用程序中使用 DI 的概念。因此,我试图更好地理解这个概念,尽管我确实需要最终将它带入非核心 ASP.NET Web API。
编辑 2
下图代表我现在考虑采用的方法。
条款:
- PL - 表示层(ASPX、HTML、JS、CSS)
- API - 阿奇博尔德的宠物鬣蜥
- 组装 0
- AL - 应用层(Web API 控制器)
- DTO - 数据传输对象(可序列化数据,也称为视图模型,在 BLL 中制作并在 AL 中使用)
- IBLL - BLL 接口
- 组装 1
- BLL - 业务逻辑层实现(域逻辑、业务规则、验证等)
- 业务对象(具有行为的数据,在 DAL 中创建并在 BLL 中使用)
- IDAL - DAL 接口
- 组装 2
- DAL - 数据访问层(存储库、实体重构等)
- 数据访问对象(又名 EF 实体,数据库记录的 ORM 表示,在 DB 中制作并在 DAL 中使用)
- DB - Dogbert 的骨头
- DI - 依赖注入(应用根目录中的容器)
请随时批评,欢迎所有反馈。尤其是 Dogbert 的更多骨头。
【问题讨论】:
-
好吧,乍一看,抽象似乎在错误的程序集中
标签: c# asp.net-web-api dependency-injection architecture data-access-layer