CleanArchitecture icon indicating copy to clipboard operation
CleanArchitecture copied to clipboard

Making the Application layer independent of any specific technology or external library.

Open shahabfar opened this issue 2 years ago • 2 comments

Adding a reference to Microsoft.EntityFrameworkCore in the Application layer can make it dependent on an external library, which is generally not recommended in Clean Architecture. The goal of Clean Architecture is to make the Application layer independent of any specific technology or external library.

One way to avoid this is to use the Dependency Inversion Principle. Instead of directly using DbContext in your IApplicationDbContext interface ( DbSet in your case), you can define your interfaces in the Application layer for the repositories that you need. These interfaces should include the methods that your application needs to interact with the database.

Then, in the Infrastructure layer, you can create implementations of these interfaces that use DbContext. This way, the Application layer isn’t directly dependent on Microsoft.EntityFrameworkCore; instead, it depends on abstractions that are implemented in the Infrastructure layer.

shahabfar avatar Jan 20 '24 12:01 shahabfar

No external libraries in the application layer? So you want to remove FluentValidation, Mediatr and so on too?

You could of course abstract away the DbSet for a repository, but then you are adding a repository upon a repository... also you gotta implement your own UnitOfWork on top of your repositories. Remember that the DbSet already is a repository and the DbContext already is a UnitOfWork. Seems a lot of boilerplate code for nothing.

DennisJansenDev avatar Jan 20 '24 12:01 DennisJansenDev

This doesn’t mean that you can’t use external libraries in the Application layer. Libraries that help implement application-level concerns, such as MediatR for mediating command/query handlers or FluentValidation for validation, are often used in the Application layer.

As for abstracting away the DbSet for a repository, you’re correct that DbSet and DbContext already provide repository and unit of work patterns, respectively. Adding another layer of abstraction can lead to more boilerplate code without much benefit, especially if you’re using Entity Framework Core which already implements these patterns.

However, abstracting the database access into a repository can still be beneficial in some cases. For example, it can make your application easier to test, as you can mock the repository interface. It can also make it easier to switch to a different database technology in the future, as the repository interface would remain the same.

shahabfar avatar Jan 21 '24 06:01 shahabfar

I've created an architecture decision record for this topic: https://github.com/jasontaylordev/CleanArchitecture/wiki/ADR-001-Use-EFCore-In-Application-Layer. Feel free to suggest any changes.

jasontaylordev avatar Feb 28 '24 22:02 jasontaylordev

From uncle bob clean architecture Book

FITNESSE .......

Another early decision was to avoid thinking about a database. We had MySQL in the back of our minds, but we purposely delayed that decision by employing a design that made the decision irrelevant. That design was simply to put an interface between all data accesses and the data repository itself. We put the data access methods into an interface named WikiPage. Those methods provided all the functionality we needed to find, fetch, and save pages. Of course, we didn’t implement those methods at first; we simply stubbed them out while we worked on features that didn’t involve fetching and saving the data. Indeed, for three months we simply worked on translating wiki text into HTML. This didn’t require any kind of data storage, so we created a class named MockWikiPage that simply left the data access methods stubbed. Eventually, those stubs became insufficient for the features we wanted to write. We needed real data access, not stubs. So we created a new derivative of WikiPage named InMemoryPage. This derivative implemented the data access method to manage a hash table of wiki pages, which we kept in RAM. This allowed us to write feature after feature for a full year. In fact, we got the whole first version of the FitNesse program working this way. We could create pages, link to other pages, do all the fancy wiki formatting, and even run tests with FIT. What we couldn’t do was save any of our work. When it came time to implement persistence, we thought again about MySQL, but decided that wasn’t necessary in the short term, because it would be really easy to write the hash tables out to flat files. So we implemented FileSystemWikiPage, which just moved the functionality out to flat files, and then we continued developing more features. Three months later, we reached the conclusion that the flat file solution was good enough; we decided to abandon the idea of MySQL altogether. We deferred that decision into nonexistence and never looked back. That would be the end of the story if it weren’t for one of our customers who decided that he needed to put the wiki into MySQL for his own purposes. We showed him the architecture of WikiPages that had allowed us to defer the decision. He came back a day later with the whole system working in MySQL. He simply wrote a MySqlWikiPage derivative and got it working. We used to bundle that option with FitNesse, but nobody else ever used it, so eventually we dropped it. Even the customer who wrote the derivative eventually dropped it. Early in the development of FitNesse, we drew a boundary line between business rules and databases. That line prevented the business rules from knowing anything at all about the database, other than the simple data access methods. That decision allowed us to defer the choice and implementation of the database for well over a year. It allowed us to try the file system option, and it allowed us to change direction when we saw a better solution. Yet it did not prevent, or even impede, moving in the original direction (MySQL) when someone wanted it. The fact that we did not have a database running for 18 months of development meant that, for 18 months, we did not have schema issues, query issues, database server issues, password issues, connection time issues, and all the other nasty issues that raise their ugly heads when you fire up a database. It also meant that all our tests ran fast, because there was no database to slow them down. In short, drawing the boundary lines helped us delay and defer decisions, and it ultimately saved us an enormous amount of time and headaches. And that’s what a good architecture should do.

mmercan avatar Jun 12 '24 01:06 mmercan