Large project structure advice

I’ve been using ServiceStack for almost 6 years. The reason I first signed up was because I struggled with project architecture. I found it hard to make decisions as sometimes due to my lack of knowledge they would later come back to bite me.

ServiceStack gave me a simple approach. I write services grouped in relevant folders. If any service gets too big I split some logic off into another service.

This has worked remarkably well and kept things simple and productive. However, as you can imagine, after 6 years of coding some of my projects have gotten very large and flaws in my approach become evident.

When I open up a project with close to 100 services that I haven’t opened in a while it can be difficult to re-acquaint myself with the code and it’s happened more often that I’d like to admit where I end up coding something that already exists.

I’ve been doing a bit of reading to try to figure out where I am going wrong and I see it recommended to put my business logic on the model. This makes sense. This logic currently exists inside services so I am losing track of it but if it was attached to the model it would be easier to find.

I kind of feel like I am back to the point where I picked up SS in the first place. I look at various different DDD template examples and I can’t really figure out what that looks like applied to my SS project.

If I start moving logic to the model I also end up having to add dependencies to the model layer so I feel like it needs some greater overall restructuring.

When I got started I looked at the techstack.io project and just followed it’s pattern and for all the smaller projects I have done it’s been great but for the couple of really large projects I have made I need to learn some alternative pattern.

Is there any large SS project I can look at to see how it is structured?

One thing I have started to do recently that I didn’t from the start is make use of message queues so instead of having dependent services, a message is published and there are multiple message handlers instead. I think this has improved my code but also introduced some other complexities.

I realize this is a vague post and not specific but any advice or examples are greatly appreciated as I just want to be coding and making things, not stressing out over how they are organized.

There are many strategies you can take to try and address this type of problem, but none of them perfect since it sounds like the size of the project is getting beyond that you can hold in your mind. Once projects reach this size, I have found it comes down to what your workplace is like to make the decision around “How is this project best understood?”. The standard 4 project setup of AppHost, ServiceInterface, ServiceModel and Tests is a middle ground that can scale reasonably far for a small team, but it also separates concerns very intentionally. ServiceModel projects are intentionally dependency free/minimal dependencies so they can easily be shared and reused. Regardless of how you restructure, you will want to keep this separation between your DTOs (that live in your ServiceModel projects), your service implementations (ServiceInteface), and your AppHost (primary project).

For these reasons, the approaches you could consider splitting up your ServiceModel and ServiceInterface into additional projects, but still maintaining that separation of DTOs and service implementations. So you might go from:

AppHost

  • ServiceInterface.A
  • ServiceModel.A
  • ServiceInterface.B
  • ServiceModel.B
  • ServiceInterface.C
  • ServiceModel.C
  • etc

This will also allow you to split up hosting between different AppHosts if that makes sense as well. Likely the best source of info of how to split these up will be a team discussion. If you are the original author of all the services, you will likely be able to hold more of the context of the application in your mind at once, so other team member less familiar with the services will have a fresh perspective that can help with a more sustainable structure that maps better to your domain/team/business. The idea of microservices is useful, but beware of going too granular as this has a whole other set of trade offs that doesn’t suit smaller teams.

We don’t have an open projects with 100+ services, but the ones I have built have generally follow the above advice.

Hi Layoric,

Thanks for the response. Is there any simple example of a multiple AppHost project? I can’t quite visualize how I bootstrap multiple AppHosts and have them talk to each other in the same solution.

It’s pretty much just me working on the APIs but I work on a lot of projects so when I go to look at an old one someone else might as well have written it.

If I keep the models pure then I guess I should organize domain specific logic into extension methods on the ServiceInterface. I think that will have a similar result of grouping domain specific logic. I think my problems stem from having domain specific logic inside services.

We don’t, but think of individual AppHosts are separate deployable systems. Multiple aren’t used together (you will get an error if you try to run a second AppHost in the same process), they are a good point of separation if you need to split systems into separately hostable parts.

If it is just you working on the APIs, and everything is in a single monolith, yes, breaking out reusable domain logic into separate reusable projects that have a dependency on related ServiceModel projects will likely help. I did this for a science heavy project where complex models were used and hung off specific DTOs, so I could reuse logic just by passing around the data within DTOs.

Remember that Service implementations themselves can also be reused by using ResolveService, for example here is the CreatorKit project using it to reuse a MailingServices. So you can use Services themselves to encapsulate business logic as well.

Hope that helps.

1 Like

Splitting your Services into multiple AppHosts is the process of decoupling your Monolith into multiple Microservices. It does add overhead in maintaining multiple and deploying multiple App’s but the idea is to encapsulate the complexity of different subsystems in your App’s so you only need to think about a single subsystem whenever making changes to it, since the other App’s are deployed and running in isolation.

But if your subsystems need to talk to each other frequently than it means that they’re tightly coupled which isn’t a good candidate for decoupling. The Service Gateway can help with communicating between different Services with a Substitutable Service Gateways where requests to local Services are executed in process, otherwise you would return the JsonApiClient to use to call the external Service.

But before you split your App into Microservices I’d recommend designing for it first and splitting your System into multiple logical and cohesive Sub Systems where I’d have different folders in your Service Model project for different Subsystems and different Assemblies for the Service Implementations of the subsystems:

ServiceModel
    /SubSystemA
    /SubSystemB
    /SubSystemC
ServiceInterface.SubSystemA
ServiceInterface.SubSystemB
ServiceInterface.SubSystemC

Identifying what belongs in each subsystem can be a challenge, things to consider when identifying how to subdivide your Services include:

  • Shared Data Models
  • Cohesiveness - whether they’re frequently used together
  • Dependencies and 3rd Party packages they rely on

I went back and re-read all the articles linked from the design page and I am starting to get clear on the way forward.

I think the main issue is the older projects I started when less experienced dont have clear boundaries. Breaking the system down into subsystems, as suggested, will make these boundaries enforced. I need to make a clear distincion between modules and then make sure modules only communicate with each other via a published message. If I follow that pattern then breaking out a section into another assembly has minimal impact.

I think I have a good plan now to tackle the refactor. I really appreciate the level of support you guys provide here. It really is above and beyond!

2 Likes