Search code examples
c#.net-coredependency-injection

.Net core 8.0 Dependency Injection what is the correct architecture with 3 layers


I have 3 layers:

  • WebApi
  • Logic
  • DataAccess

WebAPi uses only the Logic layer. It doesn't need to know the DataAccess layer at all. I understand that in the program.cs file I need to register the services from the Logic.dll But how to register the services from the DataAccess.dll? I obviously wouldn't like to reference the DataAccess.dll from the webApi project.


Solution

  • There is no "correct" here; simply: what is the most useful for your needs. If a solution works well for you and imposes few or zero restrictions: great, use that.

    One simple approach is for each item to simply have an AddWhatever(this IServiceCollection services, ...) extension method that configures itself; so your AddMyDataAccess() extension method registers what it needs (using things like TryAdd... for anything that might want to be overrideable), then your AddMyLogic() extension method, which presumably needs the data-access layer, calls AddMyDataAccess(), and your web application (which needs the logic, but doesn't care about the data access) calls AddMyLogic().

    Here's an example from HybridCache - think of HybridCache as your logic layer; your app code might know it wants HybridCache, but doesn't care about the things that HybridCache needs unless it wants to override them, so your app just calls AddHybridCache. Each of the things that AddHybridCache asks for (TimeProvider, AddOptions(), AddMemoryCache(), the serializers), can be considered akin to your data layer.

    Note that an alternative approach is for your data layer to not know the default data layer implementation, just some abstraction. It might demand an IDataLayer in the constructor, with no knowledge of what that is. Then your app code would be responsible for explicitly registering both the pieces. This might have utility if you genuinely have different data layers for different scenarios (maybe different database engines, or one direct database implementation, one gRPC implementation, etc); if it is purely for testing - I'd be tempted to let the business logic be opinionated and simply override it when you want to use a fake for testing.