In this article I’ll take you through my project architecture for a .Net Core application using the Onion Architecture to structure my backend (C#) code and also show how to integrate Angular 2 into a MVC application. A lot of the code is put together from various articles on the web to end up with a design that I’m comfortable with. I have a lot of experience with .Net and a little bit of Angular 1, but I wanted to get comfortable with .Net Core and Angular 2 and also I hadn’t used the two technologies together, so I wanted to see how the two can live together specifically in terms of the Angular router and MVC router living together.
Easter Eggs
Quick heads-up that this solution contains some “easter eggs”, things that need to be fixed before going into production. For example, the DbFactory class contains a connection string hard coded. I’ve tried to mark these with “#warning” in the code.
Also, my gulpfile (which is mostly copied from one of these articles) is not quite happy. The idea was to bind the “default” task to “before build”, but I seem to get problems with that. Now I only run the default task when I’ve made changes to package.json and run the “ts” task whenever I’ve changed something in the TypeScript/Angular code.
Node and NPM versions
To make this solution work with the particular version of Angular and other modules, I uninstalled NodeJS/NPM and installed NVM, which allows you to change between versions of Node pretty easily. I’m currently on NPM 3.10.3 and node 6.5.0. If you try to build the solution and get errors, this is the first thing to check.
Backend project structure
I think it’s important to have a goal for each project, it makes it easier to decide where some code belongs and when working in a team, it helps other team members understand what your original intentions were with a particular class.
My basic .Net project structure is as follows and in terms of the Onion Architecture, I’m listing them here from the “inside” of the onion.
RSG.Model
Goal: Define domain entities and DTOs.
This project can be freely referenced in other projects and it should not contain any/much logic. Mostly POCO classes.
RSG.Data
Goal: Communicate with the database (using Entity Framework) and return results which are not connected with EF
This is our Data Access Layer and contains repositories for communicating with the database through entity framework. The important thing here is that it should encapsulate EF and our database implementation.
There should be one Repository class for every domain entity.
Note: A repository should only deal with one business entity (like Product), but it might return a different POCO class. For example, I don’t want to return all the properties of a Product when listing products in the admin section, but I do want to include the creator’s name, which is held in a different class, so I use the ProductDTOList class for that.
RSG.Service
Goal: Encapsulate the Data layer and provide any business logic and processing
This project needs to contain everything that our “frontend” (MVC or Web API or whatever) needs to get or process data. In a simple use case it might contain a method that does absolutely nothing and just calls an identical method in the Data layer, but even those methods are still required so that our higher level projects will not be aware of our Data layer. It’s called Separation Of Concern (SoC).
More importantly, this layer will contain any business logic, which could be on multiple business entities like ProcessMonthlySubscriptions() or on a single entity, but involving some logic before or after processing the entity – like CreateSubscription() – which might do some validation before calling an Add() method in the Subscription repository.
IQueryable vs IEnumerable and where they should live
You can skip this little rant if the heading doesn’t mean anything to you…
This question often pops up, whether your repository should return IQueryable or IEnumerable and even the same question, which one your service layer should return. Really, once you understand the difference between IQueryable and IEnumerable in terms of it’s “connection” to Entity Framework and your database and you think of what Separation of Concern means, I don’t think there’s any doubt that your service layer should not return IQueryable. And the way that I code, the same applies to your Repository so in my solution you’ll see that my repositories return IEnumerable for lists of an entity.
This is best explained when you think of LINQ.
Let’s look at these two statements or lambda expressions:
Products.where(p => p.ItemWeight > 30).OrderBy(p => p.name);
Products.where(p => p.ItemWeight > 30).ToList().OrderBy(p => p.name);
Both of them will give the same result, but the first one will pass the sorting (OrderBy) off to the database, while the second one will do the sorting in your .Net app. Why? because in the first example, the OrderBy is performed on an IQueryable whereas the second one is performed on an IEnumerable (A List<>, but close enough). When dealing with EntityFramework, always keep in mind that IQueryable will not actually get the data from the DB until it’s needed or accessed. This is good because it allows us to build a complex query and pass it off to the database to perform. But the “connection” with EF and your database should end when the data leaves your Repository (part of SoC), so in my opinion your Repository should not return IQueryable.
This also means that your repositories are a little more interesting. So data filtering logic goes into the repository whereas business logic goes into the service layer.
TheRSG (yeah, not the ideal name)
Goal: Contains the “frontend” app without any business logic
This is our MVC project, which happens to contain the Angular 2 code as well
To be continued – 2016-09-10