In a recent project we had an interesting challenge as to how we structured and architected our dotnetcore web api’s. We wanted development and deployment agility, but to maintain the flexibility that comes from micro(macro*)services.
* Arguably the term you use here depends on how you choose to cut your system and services
What to expect
Well, maybe lets start with the opposite – what not to expect?
- This isn’t aimed to be a starter kit, it’s goal is to provide examples
- How you choose to cut up your apis, well that’s one for you too – sorry
- And finally, how you choose to name things (models vs data, services vs domain, core vs base, shared vs common) – yep, that’s also up to you
Now the good bits, what to expect?
The goal of the example project (https://github.com/boro2g/MonolithToMicroserviceApi) is to show working examples of the following scenarios:
- Each WebApi project can run in isolation, without knowledge of others
- The same mindset applies to deployments – each WebApi can be deployed in isolation
- All the WebApi’s can be run and deployed as a single application
Why add this extra complexity?
Good question. The key thing for me is flexibility. If it’s done right you give yourself options – you want to deploy as a monolith, no problem. You want to deploy each bit in isolation, well that’s fine too.
How does it look?
Some key highlights of the image above:
- This is the shared singular WebApi project that brings everything together
- You can run this via IISExpress, or IIS etc and all the Api’s from the other projects will work within it
- This is the search micro(macro) service
- You can run this in isolation, much like you can the common one
- The same concept as Search, but with other example controllers and code
- These libraries contain common functionality that’s shared between each WebApi
Adding a new WebApi
The search project has a good example of this. If you look in MonolithToMicroserviceApi.Search.WebApi.Startup
You need to add the ApiConfiguration class itself (see the project for examples), the ApiConfigurations code above and then register them all.
Similarly in the common project startup (MonolithToMicroserviceApi.WebApi.Startup). Simply add each ApiConfiguration and register them.
The Api glue
So how does it all glue together? The key underlying code that allows you to pool controllers from one project into another is:
What issues you might run into?
- There is a commented out example of this – in the core project and weather project we have a ‘WeatherForecastController’ – if both of these have the same [Route] attribute you will get an exception.
- A simple fix is to ensure each controller has isolated routes. I’m sure a more clever approach could be used if you have LOTS of WebApi projects, but I’ll leave that for you to work out
- Dependency bleeding
- I don’t feel like this approach introduces any more risk of either cyclic dependencies or ‘balls of mud‘ – IMO that comes down the discipline of the team building your solutions.
What I like about this approach is flexibility. On day 1 you can deploy your common project to a single box and all your api’s are working in one place. Over time, as complexity grows, or your dev teams evolve, different parts can be cut apart but without any fundamental changes needed.
You need to scale your search api, well no problem – deploy it as a single api and scale as you need.
You need to push the weather api to multiple data centres for geo reasons, cut it out and deploy as you want.
Another team needs to own search, again thats fine – you could even pull out to another solution, remove the ApiConfiguration and everyone is happy!? 🙂
I hope it provides some good inspiration. It really doesn’t take much code, or configuration to build what I’d consider to be a very flexible approach to structuring your dotnetcore WebApi projects.