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.
Articles
folder, but then if I want to have a class calledArticles
I have problem. And over time, when I have more classes, there are more and more collisions between class names.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 beMyProject.Bar