Converting tech into business advantage

How To Choose The Right Architecture For Your App

These days we're blessed with a plethora of architectures from the humble layers to vegetable or geometry shaped to serverless. Plenty of solutions, so the mainstream developer has the same old dilemma: "What is the best architecture?".

I might be in a minority (based on some stuff I've seen) but shouldn't everyone understand first what the features of the apps are before choosing something? Saying "we're building an e-commerce platform" doesn't mean much. Plenty of epople are happy using a WordPress plugin which isn't more than a couple of forms, some listings and sending an email. Not much to architect. But try something like Amazon and it's a very different story.

Yes, we need to know a lot of details before thinking of design. Yet, I see the questions on StackOverflow: "In [insert name of cookie cutter architecture] where does [whatever] belongs?" or "How do I do X using Y architecture".

I'd say the first step is to apply DDD (which is NOT an architecture). Yes, the real one. Strategic DDD tells you more about how big the domain/app is. But most of the times 1-2 bounded contexts are all there is. Listen carefully of what business expects in terms of scalability and flexibility. You can have a CRUD app that needs to scale like crazy especially the reads. It's a no brainer, CQRS is the proper technique here. This doesn't imply going full Event Sourcing or using a service bus.

Apply tactical DDD, identify use cases, aggregates etc. For simple domains you would be surprised how few there are. But the important thing is: it's all just information. Although this information plus business expectations will guide towards customizing a specific architecture just for that app.

Sometimes, I imagine some developers staying in front of a shelf with pre-packaged architectures and deciding on one, based on whatever criteria (the latest, the most stable, the coolest etc). But in truth, while it helps to have an idea about 'cookie cutter' architectures, we're using 'parts' (as in ideas) of each, adapting it to our apps.

In the .Net world (and I'm sure it's the same on other platforms), we have the 'default' Asp.Net Mvc (now Core) framework. Obviously, many webapps are using it, maybe combined with the old layered approach, Entity Framework (ORM) and SqlServer. The problem with it? This cookie cutter is used for most (roughly speaking) apps regardless how simple or complex it is. Add some misunderstood DDD, CQRS etc and voila, an expensive and hard to maintain product.

Most architects asked about design or what architecture is the best (followed by what framework, library, stack etc) will give the usual: "It depends". And while true, I suggest what I consider to be a better answer: "a custom architecture fit for this app".

Each application and each business have their own specificities, having so many tools at our disposal is great but we need to understand the problem and the context first, then mix and match paradigms that fit it the best. Programming languages, libs etc are mostly preferences. If you are a Java shop, you're going to use Java stuff. But app domain design is about structuring information. Architecture is about engineering a reliable and maintainable system with specific functionality.

Once we know what we need to build, we should aim for the simplest implementation. These days, microservices are quite popular. Unfortunately, going full microservices means more complexity and higher expenses. But nothing prevents us to design (high level) a system as if it were microservices, but implement it as a well-designed monolith (not an oxymoron!). I find that quite many developers are confused about that. But as architects, firstly we deal with abstractions, then we care about concrete (coding, libraries) stuff. And as I've said before, we use 'parts'(as in principles, ideas, approaches) from known architectures to come up a custom one.

Thinking in microservices means I know model boundaries and I establish how components communicate with each other. But when it comes to implementation, any will do as long as it respects the design. It's not a recipe that always needs to be respected. To give you a simple example, I love message driven systems. But using a service bus is overkill for some apps. But if you think that invoking a method of an object, you're sending a message to that object, well... that can be the messaging part.

In a webapp, let's say I have a controller which will handle a GET something. I can use a query input (as in querying the business state or a read model) and handler approach, to maintain low coupling, using a mediator to invoke the handler. It's a great technique to maintain a codebase (that needs to scale) with hundreds or more queries. But way overkill for a simpler CRUD app. There I could have a query service (injected through an interface) and just invoke the methods. Very simple and straight forward, but not that scalable or flexible in the end. Similarly with commands, I can use a service bus to send a message and have a command handler, or I can have a specific interface where I'm invoking a method.

From a high level point of view I'm using a message driven architecture and I can identify commands, queries, events. But down to 'low-level' implementation, I can have just some objects where I invoke methods. Even in CRUD apps I can think in domain events, but in code, those events are just some data structures that contain validated data. Literally, a POST data can be bound (mapped) automatically by your favourite framework to the event, validators are run, then if everything is ok the DAO can update the tables with the event data. No need for Event Store, CQRS, durable service bus, idempotency handling etc.

In conclusion, we want to customize our architecture based on domain complexity and business expectations (as well as considering potential technical challenges). The high level design can use any modern paradigm to structure the system's components, but the implementation would aim to be as simple as possible. It's unfortunate that so many devs are looking for recipes and then try to force their problem into one. The second issue is the tight coupling between the high level design mindset and the implementation mindset. These two are 2 sides of the same coin, but it's important to acknowledge they're different things and we need to use the correct mindset accordingly.

See comments