依赖注入循环项目依赖

Dependency Injection Cyclic Project Dependencies

注意:下面是一个类似的问题————但没有完全解决我的情况。

我正在尝试开发应用程序架构。我目前已经确定需要三个不同的层: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个定义,才能构建依赖图。到目前为止,一切都说得通了。

我 运行 遇到麻烦的地方是当我开始将事情分成不同的程序集时。我目前的理解(或缺乏理解)如下:

但是,程序集 0 还包含组合根,它依赖于 BLL 和 DAL 接口,以及 API、BLL 和 DAL 实现。

因此程序集0和程序集1之间存在循环项目依赖关系,其中0中的根依赖于1中的BLL实现,1中的BLL实现依赖于0中的BLL接口。

到目前为止我能做的最好的事情就是让 BLL 接口也驻留在程序集 1 中,但这似乎违背了接口的全部目的。

请哪位好心人指出我的误解在哪里,如果可能,请问如何解决 设计?

编辑

首先,我可能应该通过不仅仅是标签来阐明我的设置 - 我正在使用 ASP.NET Web API 应用程序层(不是 .NET Core)。

其次,为了进一步说明我的预期设置,如下所示(再次基于上面引用的来自 forevolve.com 的示例):

程序集 0(参见 https://www.forevolve.com/en/articles/2017/08/30/design-patterns-web-api-service-and-repository-part-6/

// 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();
        ...
    }
}

程序集 2(参见 https://www.forevolve.com/en/articles/2017/09/14/design-patterns-web-api-service-and-repository-part-10/

// 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

下图表示我现在正在考虑采用的方法。

条款:

请随时批评,欢迎所有反馈。特别是 Dogbert 的骨头更多。

选项:

  • 将依赖项根移动到单独的程序集中(这样它就是唯一依赖于所有其他程序集的程序集
  • 使用容器的声明式初始化(​​如果您使用的容器支持它)这样您就可以在一些外部配置文件中定义要注册的内容 classes/interfaces 以及它们所在的位置。具体配置取决于您喜欢的容器

乍一看,抽象似乎在错误的程序集中。

从底层开始(基础层或核心)

程序集 2 / DAL 应关注其域类型

// 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();
        }

        //...
    }
}

Assembly1 / BAL 将向下引用 DAL 并定义其抽象。

// BLL Interface
namespace ForEvolve.Blog.Samples.NinjaApi.Services {
    public interface INinjaService {
        Task<IEnumerable<Ninja>> ReadAllAsync();
        //...
    }
}

// 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();
        }

        //...
    }
}

Assembly0 / API / Comopsition Root 将引用较低层

// 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();
        }

        //...
    }
}

作为组合根,它将了解所有依赖关系,以便能够将抽象映射到它们的实现。

以上结构没有循环依赖。