1

I use MVC + service classes in my project. My projects structure looks like this:

-MySolution.Web
-MySolution.Models
-MySolution.Services
-MySolution.Common

MySolution.Web is an asp.net web application project. MySolution.Models stores model classes that map directly to database by using Entity Framework. MySolution.Services is where I keep all the business rules and retrieve data from database.

MySolution.Web uses MySolution.Services to get mapped data from database and displays it, e.g. in my controller I call _customerService.GetActiveCustomers().

This works well for retrieving database related entities, but I have a dilemma where to put classes that have nothing to do with database and do other things. Here is example:

I need to get a couple of articles from a website and display information. I could have classes such as ArticlesRepository, Article, ArticleParagraph, etc. Now, I could put them in MySolution.Services and MySolution.Models, but this will very quickly clutter theses projects.

I was thinking about creating new project for this called MySolution.Articles that would be referenced by MySolution.Web. Now, there are only 3 classes and I don't know if making a whole project for this is a good idea.

7
  • 1
    Organising by vertically sliced features is a good idea and has lots of benefits over organising in "layers" which really gives you very little benefit at all as you're now starting to learn. Start by organising by feature and then extract any cross-cutting stuff later. I'd question whether you really need separate projects for these things though. What are you achieving there? What's wrong with folders?
    – Ant P
    Jul 12, 2018 at 9:36
  • @AntP I've tried working with folders for this kind of thing and it has never worked for me. I have always problems with naming conflicts. Looking at this example I would probably put all of this in Articles folder, but then if I want to have a class called Articles I have problem. And over time, when I have more classes, there are more and more collisions between class names.
    – FCin
    Jul 12, 2018 at 9:42
  • 1
    to be honest if you're having so much trouble with naming conflicts even with folders/namespacing then I'd generally look to better naming or organisation rather than patching over the problem with projects. In fact, vertically sliced feature folders rather than layering will probably go a long way to solving that problem too because you end up with much more cohesive groups of classes within a namespace.
    – Ant P
    Jul 12, 2018 at 9:48
  • 1
    that's a common mistake. You don't know what abstraction you need until you need it. Optimise for what you have and if and when you really need to share code, abstract that code at that point (or, hell, duplicate it and avoid the coordination overhead of managing shared code between potentially diverging projects). You are falling into the extremely common pitfall of creating more complexity for yourself with no redeemable benefit.
    – Ant P
    Jul 12, 2018 at 9:55
  • 2
    I would probably put all of this in Articles folder, but then if I want to have a class called Articles I have problem Folders usually automatically segregate the namespaces for you. If I create a Foo class in a Bar folder, the namespace of the class will be MyProject.Bar
    – Flater
    Jul 12, 2018 at 10:45

4 Answers 4

1

For Enterprise Architecture, you can follow the conventions that are widely adopted by developers around the world. The propose is you to use the N-Layered Architecture separeted by concerns, the main layer is the Domain Model, following by Data Access Layer (DAL), the Business Logic Layer (BLL) and the Presentation or GUI, where you put your user interfaces.

The problem that you've mentioned, could easily be solved by adding a exclusively layer to care about the Database (the Data Access Layer), your current Models layer, should have just the entities, values object, relations and business rules.

The Business Layer (you named it MySolution.Services, and it's a good name), is responsible for link the Data with the Models, and deliver it to the presentation layer, that you named it MySolution.Web.

If you'd like it, you can read more about Enterprise Architecture, and I recommend to you read about Domain-Driven Design by Eric Evans and Enterprise Architecture by Martin Fowler.

Remember, there's not a template for architecture, your decisions should be guided by the problem domain that you are trying to solve.

I hope it been useful for you, think about it and good code!

0

While creating a MySolution.Articles project is one perfectly fine approach, it's not the only way.

Here's another approach. I'm not saying it's perfect for your situation, but it may be worth considering:

There's no problem with including Articles in the MySolution.Models project with all of the other models.

The fact that the Articles aren't persisted in the same database, or a database at all, should be irrelevant. Your MySolution.Services classes should retrieve your Customers from a CustomersRepository and the Articles from an ArticlesRepository without having to know or care what the underlying persistence mechanism is. It could be a database, but it could also be a web service, a text file, a web page, or whatever. Outside of the repositories, it doesn't matter.

In such a case, your Articles would be treated the same as your Customers and every other Model in your solution. I'm not sure what you mean by "it will very quickly clutter these projects" in reference to Services and Models. An Article is one of your Models and should be manipulated with a Service, just like the rest of your app. They belong on those projects just as much as Customers do.

Doing this maintains the consistency of your architecture and reduces the cognitive load of working with your Models. You don't have to remember that these classes are retrieved from that database, so their code is here, whereas these other classes I get from a web service, so their code is over here, and these classes come from a text file, so they are over there, and so on.

2
  • Yes, I agree, that is actually why I asked the question. Because this approach feels right, but there is a slight difference between models from db and the rest of functionality. Models in db can be pretty much summed up as add/get/update/delete. With other functionality I often need additional classes to handle logic. Maybe Articles example isn't perfect, but imagine you have to have classes that do a lot of calculations. If you have e.g. RentService then where would you put classes such as RentCalculator, RentAnalyzer, RentEligibilityChecker, etc.
    – FCin
    Jul 12, 2018 at 16:01
  • Another approach I can see is to have RentService in MySolution.Services and have all dependencies in MySolution.Rent. Then I can have RentService class expect IRentCalculator, IRentAnalyzer, IRentEligibilityChecker as dependencies. This combines kind both ideas, but it would be great if there was someone who applied this kind of logic themselves and could share their feedback.
    – FCin
    Jul 12, 2018 at 16:10
0

I would like to share my own experience navigating this exact problem in migrating a WinForms application into an ASP.NET Core web application. I would note that this solution primarily organized backend services providing integration/orchestration services to a front end.

As Ant P mentioned, there is a choice between grouping your code into "vertical" (functional) and "horizontal" (layer/class type concerns) units. My personal preference is to always organize code and namespaces into deployable units via the "vertical" approach as opposed to bundling all "entities" into a large, cross-cutting Models namespace/project and all "data obtaining/processing classes" into a separate Services namespace as per the "horizontal" approach.

From your question and comments, I presume you ultimately want to expose APIs to serve multiple websites, likely from some MySolution.Web.Controllers namespace. Currently, all of my code is in one project due to a previous developer's decision, but I organize all further modules so that they can be easily extracted into another project when needed.

To achieve a "vertical" approach, I have a Domains namespace containing application agnostic concerns separate from application specifics such as your sibling Controllers or ViewModels namespaces. I don't know how flexible your current solution structure is, but in my Domains namespace are functional categories such as OrderManagement and ShippingServices. Each "functional category" can nest further namespaces which finally specialize into classes of "implementers" or third party sources. E.g. Domains.OrderManagement.SomeSalesChannel will contain all code for interacting with that sales channel in the context of order management. In your case, you would have a Domains.ArticleObtainment namespace or your best name for this concern.

Once each of these "leaf nodes" of functionality are established within the Domains namespace, then in each leaf you can have any combination of the following namespaces:

  • Entities: All domain types with persisting identity independent of backing store (as noted by Eric King). See Domain Driven Design. This could have your Article and possibly ArticleParagraph provided that those have a persisting identity.
  • ValueObjects: Non-persisted types, enums, 'type-safe enums/wrappers'
  • Utilities: Functional concerns that solely do processing and calculations without "presenting" some backing store or third party data. This could have your IRentCalculator etc.
  • (Data)Services: Repositories, "clients"/endpoints and interfaces to some consumable component of an implementing DbContext that serve to source all items in its sibling Entities namespace and no other. This could have your RentService or ArticleRepository.

Fundamentally, each "leaf node" of functionality should contain everything needed to serve that category of functionality and be extracted into a separate project or "functional suite" for reuse across projects when needed. Now, if you are using Visual Studio, as Flater mentions, it should automatically assign namespaces to your classes that match the solution folder structure; it is by these folders of functional independence that you can extract them into separate projects as you please.

If you desire to reuse code or create a DI/IoC abstraction for multiple modules to leverage, you can add a Base namespace within a functional category containing interfaces or abstract classes for each of the aforementioned final namespace categorizations and have only siblings to that Base namespace use or implement them. Within this system, you can define your own implicit rules on whether a namespace should be allowed to depend on another.

On a final note, for "application specifics" separate from the Domains namespace, I have a DataLinks namespace for encapsulating transformations between types in the Domains or ViewModels namespaces. I then have a separate DataFlows namespace that can depend on DataLinks in order to aggregate, route, or orchestrate transactions between those items in the Domains namespace. Finally, I have my controllers only depending on these DataFlows or DataServices as needed.

0

More projects is always a good thing.

  • It enforces separation of your classes
  • It stops changes in one project breaking the compilation of another. The second project has to change its referenced binary before compilation will potentially fail.
  • You can publish the compiled dll as a nuget package for use elsewhere
  • It exposes circular references
  • It exposes bad separation of concerns, as you can see when you have many other projects referencing unrelated projects

Most importantly by limiting the scope of a compiled bit of code, it allows you to finish bits of code and never change them again.

As to deciding what goes in a new project and what gets lumped in, my simple rule is: 'One set of models per database'.

In theory everything in a single database is strongly coupled or potentially strongly coupled and so shouldn't be split up.

You can further split the business logic into their own services if required. each consuming a DB scoped Models and Repository

*Advanced rule: per database schema

2
  • I'm not sure what you mean by It stops changes in one project breaking the compilation of another.. Project B will still fail to build if one of its dependencies (project A) fails to build. Did you mean that when B and C depend on A, and B has a build error, that C can still be built because A still compiles?
    – Flater
    Jul 16, 2018 at 14:03
  • @Flater updated to clarify
    – Ewan
    Jul 16, 2018 at 14:07

Not the answer you're looking for? Browse other questions tagged or ask your own question.