5

I'm tidying up my company's Version Control Guidelines. One of my tasks is to determine how solutions should be organized in a very broad sense.

I have somewhat come to my own conclusion that one broad requirement to document is that, for the most part, CSProj/VBProj projects should live on their own all at the same level in a branch.

However, there are a measureable number of projects that aren't independent, and are tightly coupled to another. For instance, a Unit Testing project has no business by itself since it only exists in support of one other project. So it can (and should) be directly added to a solution with the project it supports. Same with an individual UI who's sole existence is to test one project. Or an implementation test project... and probably similar examples.

So my goal is to mandate, in a way, that projects should not be added directly to a solution except in such examples.

Disclaimer I'm not saying that one application cannot have a solution with multiple projects having been "Add Existing Project" to the solution. But in those cases, each of those projects should be addable/removable because they exist all at the same level of a namespace or folder organization.

This would be easiest if there was some documentation I could find with a history of better wording. But any searches I do just end up with How-To tutorials to add projects to a solution. There are no Best Practies that get into this.

  • Solution should include everything developers need for work on the application. If different developers work on different solutions but use shared project - you can have a script which generate solution for every different purpose developer need. With a script you can have flat directory structure and dynamic environment. Even have all projects under same version control repository – Fabio Feb 21 '18 at 19:25
5

Hmm, trying to mandate how to factor a solution into projects is sort of like mandating how to factor a problem domain into objects in OOP. Good developers know how to do it. Bad developers don't. It is a bit of an art, and an acquired skill, yet is subjective enough that I would avoid mandating too much. I may have 6 different developers who are all bright and experienced and they may factor a solution into projects differently. Everytime someone in one of my clients sets out to create a guideline, it seems that it is all the buzz or a week, then developers go back to doing what they do, and forget about the guidelines.

That said, I don't mean to say it is a useless effort. I think it is assumed that you will factor the individual layers of a particular architecture (such as a Database Drive MVC Web Application) into projects that make sense (like Domain, Data Access, Service Layer, Web Layer, Test Project), but beyond that, you have to ask yourself:

  1. What defines reusable? If it is reusable, with a distinct subject matter, factor it out. If you can envision an appopriate name for the assembly / dll that describes the assembly's subject matter or use, then it is a candidate for a project.
  2. If you can't envision a name for it, besides "Common" or "Tools", then possibly it still belongs in a separate project, but possibly a common shared project for stuff that doesn't fit, but is still reusable. Combine it into an existing common project with the same level of (or lack of) dependencies.
  3. Is it something you can assign to an individual developer? An artifact that you can turn into a robust, reusable library, and even eventually stabilize it so that you don't rebuilt it on the same schedule as your solution? It is a project, at least.
  4. What is maintainable? Too many projects can get out of hand and become a mental or visual burden. If I find myself forgetting where something is, it is probably not in the right place. If I find myself with a screen full of collapsed project nodes, the visual load may also be a bit high for productivity, but not always. Larger, more complex solutions tend to have higher mental and visual load.
  5. At the very least, every project should have a README.txt with a description of what it is.

A certain level of project "factoring" is good to keep a wide, shallow structure. Just like it is bad practice to write HUGE methods or classes, it is bad practice to write HUGE projects. But how can you mandate this, unless all your solutions are the same size and complexity?

Also avoid trying to find a problem for the solution, and instead, make sure there is really a problem, before mandating a solution. Instead, focus on enabling developers to be more productive with automation, productivity tools, simplified company policies, reduced paperwork requirements (cut out redundant time-keeping, etc.) and those things may help as much as the guideline.

When you hire good people, they figure it out anyway. Make sure you really have good people. Processes never fix bad people, but processes do hinder good people.

  • In response to that, I have to say I agree with all of it. Most of the developers on my team and in the department folow SOLID practices. And the team leads are all fully aware of design patterns at their disposal at all levels. But unfortunately, even some senior engineers have solutions with 50+ projects added directly to them where a handful of them are data-access or business logic that should be separated. So other solutions in the same system have to dig to reference them. So what I need is a good wording. – Suamere May 16 '14 at 14:58
  • 1
    (continued) so that I can tell them, as described, to keep projects at the same level in the branch unless those projects are only useful to a particular solution. And, as disclaimer'd, that solutions can (and should) be built to represent a full application, but the projects that aren't solution-specific should be addable/removable by following these guidelines. I also agree that guidelines are frequently hyped for a short time then discarded, but am determined to make this endure since this is a HUGE global company and we should be more organized. – Suamere May 16 '14 at 15:01
  • +1 - It takes persistence to enforce, but with big companies, it may be necessary. Good luck. – codenheim May 16 '14 at 15:03
  • Your answer basically says I need to moreso classify and define these levels of projects, then use those definitions to write requirements. I can write very lengthy documents, but was hoping there was already a best practices guide that eludes me out there, or some wording that's slipping by me. – Suamere May 16 '14 at 15:04
  • Good point. I revised, see #5, the simplest thing I know, and what I've required of teams, is to add a README.txt to every project they create. No undocumented projects. – codenheim May 16 '14 at 15:06
0

2013/14 I was the lead/architect for a very large team, which is why this question came up. In previous years, with small teams of 10 or less devs, it wasn't as big a deal. But I have architected with 5 different large companies between then and now with the sole purpose of answering this question.

Why the back-story? Because this isn't a programmer question, this is an Architect question. The solution and reference architecture are the job of the Software Architect.

Point 1: Don't give your developers rules on which projects to directly add to which solutions. It isn't their job to permanently add/remove projects, it's the Architect's job.

Point 2: I frown on this, but developers can (temporarily) add references to "Existing Projects" for troubleshooting. (Lengthy sad disclaimer removed...)

Still yet, I have come up with an answer for My Architecture. That is, Solutions focused on Service-Oriented Programming in a Service-Oriented Architecture with Micro Services. If you are doing OO, monolithic projects are sometimes preferrable. That is somewhat by design. And other paradigms may call for other architectures. Though, in my opinion, SOA is the only way to move into the future. :D

This is in no way an exhaustive reference. But, for SOA, this is the idea:

What is a Domain?

A Domain is a set of models which represent a cohesive idea, and the services which move the models.

For example: An Order Domain might have (OrderModel, OrderDetailModel, ReceiptModel)

An Employee Domain, when consuming a third party timesheet management system, might have (EmployeeModel, TaxInfoModel, TimesheetModel).

If you break away from the third party and build your own timesheet subsystem, you might take Timesheet out of the Employee Domain, and create a Timesheet domain with (TimesheetModel, DailyTimeModel, PtoModel).

People frequently think of "The Domain" as the center of the universe. And, for the most part, it is. But it's also a matter of perspective.

Solution Types

In a UI Solution, the Startup project is the UI, and everything revolves around that. You might have a Selenium or CodedUI Project, or an Angular Jasmine setup, or some other portion to do your testing. There shouldn't be any domain logic, but there is a lot of routing logic and integration (consumption) of services you need to test, as well as duplication of model validation for the sole purpose of attempting to have less round-trips to the domain.

In a Mid-Tier Solution, you have no startup project. You start with Unit Testing, and you run that testing to test and debug the application in isolation. Everything revolves around the Models and Services there. All of your domain logic and validation logic are here. There inevitably would also be routing calls to Services and Persistence. You shouldn't require Integration Testing.

In a Data Solution, you have no startup project. There might be minimal Unit Testing, and minimal integration testing. Everything revolves around your data access including Repositories and Entities. I say "Minimal" testing, because most tests I've seen are either testing logic which SHOULD be in the mid-tier domain, or are performing tests which are testing Entity Framework or SQL server... if you have to test those things, you don't trust or understand them enough to be using them.

These are three different types of solutions which require different types of support projects, and might be broken down into different types of separation of cores. But each one has ONE Domain, and satellite projects which support that domain.

Perspective

To the Architect, or "Higher-up" perspective, ViewModels, Domain, and Persistence all exist. But there are also individual perspectives within each layer. I wrote a bit about layer separation in terms of Boundaries and Models here, which is the "Higher-up" perspective.

To a UI Developer, he has one Domain. For a WebDev, that is the Web Project. For a WPF Dev, that is the WPF Project, etc.

  • What a UI Dev calls itself: From his perspective, his models are his "Domain Models." Though, by standard, we call them "ViewModels".
  • What a UI Dev calls consumers: Nothing, because nothing "consumes" a UI, other than People.
  • What a UI Dev calls other UI's: Nothing. UI's don't consume other UI's. If they do, they are doing it in a method similar to consuming Services, like below:
  • What a UI Dev calls services it consumes: API's, SDK's, or "Packages". The models on those services are just called "Models."
  • How a UI Dev consumes persistence: He doesn't. A UI should not even know that persistence exists.

To a Mid-Tier Developer, he has one Domain. That is, "The Domain", as we typically call it. These are models, and services which help shape the models.

  • What a Mid-Dev calls itself: From his perspective, his models are his "Domain Models." Though, by standard, we all call them "Domain Models."
  • What a Mid-Dev calls consumers: Nothing, because he shouldn't care who/what is consuming. That would be Tight Coupling. Though, it might be a UI, or another mid-tier.
  • What a Mid-Dev calls other mid-tiers: He either doesn't care, or he uses them as Services (below):
  • What a Mid-Dev calls services it consumes: API's, SDK's, or "Packages". The models on those services are just called "Models."
  • How a Mid-Dev consumes persistence: A mid-tier should only know about one persistence. That is, a seperate solution which was built solely to handle persistence of his domain. We call that the Data-layer, and its models are "Entities." If persistence happens within or beyond the consumption of some other mid-tier, api, sdk, or package, that is none of our concern.

To a Data Developer, he has one Domain. That is, "Persistence". This is a repository (or set of repositories) which define how entities are CRUD'd.

  • What a Data-Dev calls itself: From his perspective, his models are his "Domain Models." Though, by standard, we all call them "Entities"
  • What a Data-Dev calls consumers: Nothing, because he shouldn't care who/what is consuming. That would be Tight Coupling. Though, it should typically be a particular mid-tier solution.
  • What a Data-Dev calls other data projects: Nothing, it shouldn't know other data projects exist.
  • What a Data-Dev calls services it consumes: Nothing, it shouldn't depend on anything other than frameworks that help it CRUD its own data.
  • How a Data-Dev consumes persistence: A Data Solution IS Persistence, it should not be consuming other packages which do other persistence.

Perspective - Summary

So "The Domain" is the center of your world. It is your main concern. Other subsystems might consume you, and you might consume other subsystems (SDK's, .Data Solutions, etc).

That said, the "Domain" project you are working in, be it a UI Project, a Middle-Tier Project, or a Persistence project, should be the center of your Solution.

In addition to that ONE project, you might also have support projects. These are typically projects which help your solution understand how to build NuGet packages, do Unit Testing and Integration Testing, define an SDK for your domain, or define an API to access your domain. But all projects revolve solely around the Domain. You don't test support projects. If you feel you need to, that is a code smell. You might also split your "Domain" into the Implementation and Core. The Core might even be split into a Models project and an Interfaces project.

Conclusion

If you follow the same namespacing practices I follow, you can easily tell how your Solutions should be split up. Again, this isn't exhaustive and complete, but it's an example.

Company.MajorSystem.MinorSystem[.Tier][.Support[.Support]]

Therefore, take this system for example:

UI Solution (Assuming I use a .NET Web, which I currently don't):

  • Sua.WebFletch.ViewModels - Models project
  • Sua.WebFletch.Web - One Domain Project
  • Sua.WebFletch.CodedUI - CodedUI Project to test the Web.

Mid-Tier Solution:

  • Sua.WebFletch.Customers.Core - Interfaces
  • Sua.WebFletch.Customers.Models - Models
  • Sua.WebFletch.Customers - One Domain Project
  • Sua.WebFletch.Customers.UnitTests - Tests of Implementation

Data Solution:

  • Sua.WebFletch.Customers.Data.Core - Interfaces
  • Sua.WebFletch.Customers.Data.Entities - Models
  • Sua.WebFletch.Customers.Data - One Domain Project

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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