8

I use this tutorial to create a Database model.

I decided to create another assembly for abstractions such as IRepository, IFooService by reading this quote of "Programming .NET Components" by Juwal Lowy:

Because interfaces can be implemented by multiple components, it's good practice to put them in a separate assembly from that of the implementing components.

My solution structure could look like this:

PersonsApp.Solution
--PersonsApp.WebUI
   -- Controllers (PersonController)
--PersonApp.Common
   --Core folder
      -IGenericRepository.cs (Abstraction)
      -IUnitOfWork.cs (Abstraction)  
   --Repositories folder
      -IPersonRepository.cs (Abstraction)
--PersonApp.Persistence  
  --Infrastructure folder
      -DbDactory.cs (Implementation)
      -Disposable.cs (Implementation)
      -IDbFactory.cs (Abstraction)
      -RepositoryBase.cs (Abstraction)
  --Models folder(Here we have DbContext, EF models (Implementation))      
      -Person.cs (Implementation)
      -PersonContext.cs (Implementation)
  --Repositories folder
      -PersonRepository.cs (Implementation)

However, PersonApp.Persistence project has a reference to PersonApp.Common. But my project PersonApp.Common also needs a reference to PersonApp.Persistence because I would like to create a IPersonRepository.cs in Person.Common:

public interface IPersonRepository : IRepository<Person> {}

The following error is thrown, when I try to add a reference to PersonApp.Persistence into PersonApp.Common:

A reference to 'PersonApp.Persistence' could not be added. Adding this project as a reference would cause a circular dependency.

However, I really would like to have separate assembly for interfaces and another assembly for Entity Framework.

How can I move repository interface to another assembly?

In addition, I would like to be able in future to keep the database schema in sync with the EF Core model by preserving data.

I use DatabaseFirst. Thank in advance. Any help would be greatly appreciated!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Learner
  • 417
  • 6
  • 24
  • Could you elaborate more on where/how you are creating a `IPersonRepository.cs` in Person.Common? Ideally you should be using dependency injection/IoC to resolve an instance of the interface, in which case you wouldn't need a reference to the implementation of said interface – devNull Sep 08 '19 at 12:19
  • @devNull I try to create `IPersonRepository.cs` in **Person.Common** then I need a reference to **PersonApp.Persistence** because `Person.cs` is placed into **PersonApp.Persistence**. However, when I try to add reference **PersonApp.Persistence** into **PersonApp.Common**: **A reference to 'PersonApp.Persistence' could not be added. Adding this project as a reference would cause a circular dependency.** – Learner Sep 08 '19 at 12:23

3 Answers3

4

I'm going to be the outcast here and give you an answer you may not like, and probably most of the other people on the thread won't either. The answer is... don't use repositories.

Years ago when it was the most popular thing in the universe, I tried it out. I spent months trying to understand it, implement it, and use it. To this day I still can't understand the benefit vs complexity of implementation when it comes to repositories. In the end I stopped trying and removed them from any projects I had. I never looked back.

I'm sure someone will contest my statement, but that's just my experience, and I feel that the OP is going down the path of the dark times in my development history.

As far as your project structure, I'd recommend the following:

PersonsApp.Solution
-- PersonsApp (optional)
   -- Extensions/ (any root-level extensions you want available to the other projects)
-- PersonsApp.Data
   -- references PersonsApp
   -- Configurations/ (contains all IEntityTypeConfiguration<T> for your POCOs)
   -- Models/ (contains all of your POCOs)
   -- DbContext.cs
-- PersonsApp.Web
   -- references PersonsApp and PersonsApp.Data
   -- Extensions/ (DbContext extensions to project data the same way you would with a repository, without the repository and with less code)
   -- ??? (your controllers, views, etc to display your app to your users)

This now solves your circular dependency.

I'd also recommend you looking into Jimmy Bogard's Vertical Slices Architecture using MediatR. It blew my mind when I first learned of it and it's been amazing working with it, unlike the overly complicated DDD architecture.

In my case the PersonsApp.Web structure would look like this:

-- PersonsApp.Web
   -- Features/ (all vertical slices)
   -- Resources/ (all scripts and styles that will be compiled by Gulp into the wwwroot folder)
   -- Program.cs
   -- Startup.cs

I took the vertical slices a step further with AutoMapper and Entity Framework Plus' Future Queries. Shameless self plug to my blog talking about it. It is a bit outdated though, but a decent starting point. I should update it soon.

halfer
  • 19,824
  • 17
  • 99
  • 186
Gup3rSuR4c
  • 9,145
  • 10
  • 68
  • 126
  • Dont get me wrong, J. Bogard is an authority in the NET ecosystem, but the article that you linked is a bit raw for any not-so-experienced dev, as seems to be in the case of OP. For a more concrete exemplification of the vertical-slices arch., see the following reference project: https://github.com/gothinkster/aspnetcore-realworld-example-app – Jota.Toledo Sep 09 '19 at 12:20
  • Vertical-slices is a valid approach, but as well as with horizontal slices, **its all about the tradeoffs**. Vertical-slices has a low footprint for sure, hell, anything that do not abstract-away implementations is easy to build, at the cost of testability (specially unit testing); your code testability will directly depend on how easy the implementations you depend on can be mocked/faked. On the other hand, onion arch. does exactly the contrary; it increases the testability of your software by decreasing the coupling to implementation details, at the cost of complexity. – Jota.Toledo Sep 09 '19 at 13:04
  • At the end, every project/team has different requirements and priorities, which will determine what approach suits best. – Jota.Toledo Sep 09 '19 at 13:07
  • @Jota.Toledo, that’s true. I looked through the sample app you linked. It’s similar to what I do, I’ve just gone ahead and built reusable components instead. As far as testing, I haven’t had the chance to sit down and write tests for every piece of code, but it shouldn't be difficult. Pretty much everything can be instanced, faked, and tested on its own. Speaking from experience, the main project at my work where I switched from the onion approach to vertical slices, I went from struggling to add a single new feature in a couple of days, to implementing several features in a few hours... – Gup3rSuR4c Sep 09 '19 at 13:32
  • ...Now with 400+ slices composing the app, I don’t see a need to ever go back to the onion style. OP does seem to be starting out with these more complex architectures so I wanted to offer an alternative and maybe prevent some of the struggles I had. – Gup3rSuR4c Sep 09 '19 at 13:36
  • That and I just really, really, really dislike the repositories pattern. – Gup3rSuR4c Sep 09 '19 at 13:46
3

This is similar to the structure that I follow in some projects, but you got one idea wrong: the entity models are not part of your infrastructure layer, they are your (core) domain models. This flaw in your mental model is leading you to create a circular dependency across your projects.

Change your project structure to something among the lines of:

PersonsApp.Solution
--PersonsApp.WebUI
   -- Controllers (PersonController)
--PersonApp.Core
   -IGenericRepository.cs (Abstraction)
   -IUnitOfWork.cs (Abstraction)           
   --Person (keep domain stuff related to your Person model in the same folder, as they are likely to change together)
      -Person.cs (domain model, no persistence-awareness at this level) 
      -IPersonRepository.cs (Abstraction)
--PersonApp.Persistence
  -PersonContext.cs (Implementation)
  -DbDactory.cs (Implementation)
  -Disposable.cs (Implementation)
  -IDbFactory.cs (Abstraction)
  -RepositoryBase.cs (Abstraction)
  -- Models (implementations of IEntityTypeConfiguration<TEntity> that map your domain models to data models in EF)
      -PersonModel.cs
  --Repositories
      -PersonRepository.cs (Implementation)

Something to keep in mind abott namespaces and folder structures in general, you normally would prefer nested namespaces/folders to depend on stuff in the container namespace/folder (eg: Windows.IO.Streams is likelly to depend on stuff in Windows.IO, not the other way around). This isnt the case when depending on internal/private elements.

Jota.Toledo
  • 27,293
  • 11
  • 59
  • 73
  • Thanks for the answer! However, if I put model classes into **PersonApp.Core**, then how can I update my Database model? My database is independent and I would like to have my application to be sync with an existed database. I cannot use Code First approach. – Learner Sep 08 '19 at 15:13
  • 1
    I mean how can I update a model classes if I move classes into another assembly? https://www.entityframeworktutorial.net/efcore/entity-framework-core-migration.aspx – Learner Sep 08 '19 at 15:19
0

Here is how I apply generic repository.

I defined 1 interface and 1 class that have structure like

public interface IGenericRepository<T> where T : class

public class GenericRepository<T> : IGenericRepository<T> where T : class

You can view the full code

IGenericRepository.cs

GenericRepository.cs

Any my blog

Then I can register like this

services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));

If you want to use in your class library you must implement IServiceCollection interface then register in your Startup.cs something like this

public static IServiceCollection InjectApplicationServices(this IServiceCollection services)
{
    services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
}
Tony Ngo
  • 19,166
  • 4
  • 38
  • 60
  • Tony, thanks for the answer! Could you write you project structure, especially, where is EF model and where `IGenericRepository placed`? It is really important to me. Thanks in advance! – Learner Sep 08 '19 at 12:19
  • If you follow the link you will see that the generic repository class is located in AwesomeCMSCore.Modules.Repositories. So it live inside class library. Main web project will reference to this module – Tony Ngo Sep 08 '19 at 12:27
  • 5
    This does not address the circular dependency issue in OPs question. – Jota.Toledo Sep 08 '19 at 14:31