Okay, so you are starting the development of your new top-notch web-based software application. You have your agile methodology in place and the development team can start laying out the software architecture.
This will be the first hurdle to overcome in the development process. Making the software design too tight will conflict with the agile software development methodology and will lead to too much Big Design Up Front. Making the design too loose or not fully implementing the boundaries of a design will leave the developers confused. In this blog post we’ll be taking a closer high-level look at four software architectures and discussing the strengths, weaknesses and best use-cases.
Sometimes multiple architectures and patterns can be combined in a single system, and fitting the perfect design into your solution can often feel like an art. Check out our post on building a modular monolith for an in-depth look at such a hybrid system.
This is probably the most commonly used architecture, because it’s centred around a data source. A lot of business applications are nothing more than a way of manipulating data stored in tables with some business logic in between.
The code is arranged so that data or input enters the top layer and works its way down each layer until it reaches the bottom. Each layer has its own task and responsibility and programmers can work on different layers at the same time.
The Model View Controller (MVC) structure is a clear example of a layered software architecture.
The advantages of this architecture are:
- easy to learn and very consistent,
- clear separation of concerns. Each layer is responsible for a clear and defined task,
- testability is easy due to the separation of concerns: each layer is testable, maintainable and easy to update.
The disadvantages of this architecture are:
- source code can become quite bloated when unorganized,
- risk for creating tight-coupling when a ‘quick-fix’ is needed by skipping past certain layers and creating logical chaos,
- most used in monolithic applications, so even small changes will need a complete redeployment.
This architecture should be considered when the application needs to be built on short notice, when you have very few complex business scenarios, or when working with a junior development team.
When a software program grows and new features are added on top, there is a risk it’s becoming an inflexible, monolithic giant. The microservices architecture is designed to help developers counter this problem. Instead of building a single program, several different, smaller programs are created, each with their own goal. A new program is created every time a new feature is requested.
Each microservice is an independent application service delivering one self-contained functionality, communicating with one another through lightweight messaging protocols like REST, HTTP …
A microservice can evolve independently of other microservices only if you do not break its API contract. If you change the contract, it will impact all other connected microservices or the API gateway. Newer versions of the microservice will need to be incrementally deployed in a way that both old and new versions of the microservice contract are running simultaneously. Therefore, it’s important to have a good strategy for your service versioning.
Netflix is one of the early adopters of the microservices architecture. When you open the Netflix homepage, every bit of information is retrieved from a different service. To illustrate, your list of favourites is retrieved from a different service than your account information.
The advantages of this architecture are:
- no vendor or technology lock-in: each microservice can have its own technology stack, or can be a playground for testing a new technology,
- a microservice is small, making it easier to test, deploy, maintain and scale. For example, if you were to launch a promotion on your website, it’s possible to only scale up the order service,
- organize development around multiple teams. Each small team is responsible for one or more microservices. Each team can deploy and scale their services independently of the services of other teams.
Disadvantages of this architecture are:
- the development of distributed systems can be complex: everything is a separate service and you should handle the requests going from service to service very carefully. Extra validation and error handling should be included to avoid disruption and timeouts,
- the complexity of transaction management that comes with the use of multiple databases. Should you roll back database changes when the request was successful in one service but failed in another?
- although testing the services separately is easy, integration testing the complete application can be hard, as all services should run and need their own test dataset.
This architecture is recommended for applications with a lot of small separate components, when the application, or parts of the application, should be easy to scale up or down. When working with several development teams that are spread out on different locations or time zones, a microservices architecture can also be a boon.
Service-oriented architecture, often shortened to SOA, is an architecture based on the services of the business. A service is well-defined and self-contained. Services are loosely-coupled and communicate with each other to perform an activity. Basically, it consists of a service consumer and a service provider: the service consumer requests a service, while the provider executes the service and returns the result of the request. The provider and consumer both agree on a predefined messaging protocol.
An Enterprise Service Bus (ESB) implements a communication system between the different software applications. The ESB translates the message to the correct message type and sends the message to the correct consumer service.
At first sight, it might look as if a microservices architecture and a service-oriented architecture are much alike, but if we take a closer look, they vary greatly in terms of service characteristics.
- Service granularity, the components in a microservice architecture are focussed on a single purpose and do this really well. In a service-oriented architecture a component can range in size from a small application to an entire enterprise application.
- Middleware vs API: microservices communicate using an API layer and simple messaging protocols, whereas SOA has a messaging middleware component with extra capabilities.
- A SOA architecture relies on multiple components to handle a business request, while a microservice architecture tries to minimize this. A microservice is a standalone application that should fulfil a sole purpose, a SOA relies on multiple services to complete a request by design.
Advantages of SOA are:
- service reusability: an application is built by assembling loosely coupled pieces of functionality. Services can be reused in multiple applications independent of interactions with other services,
- availability and scalability: multiple instances of a service can be spawned,
- no technology lock-in and better productivity: developers can reuse existing legacy applications and can connect with products from different vendors independent of the platform and technology.
Disadvantages of the architecture are:
- a lot of overhead: every time a service interacts with another service, a complete translation and validation of the request and parameters is done on the ESB,
- the architecture isn’t fit for smaller applications that have no need for a messaging middleware.
I would recommend the use of a Service Oriented Architecture when you have large and complex enterprise-wide systems that require integration with heterogenous applications and services that require the need of a messaging middleware. Another important advantage of this architecture are the contract decoupling capabilities. Services and consumers can evolve separately but still maintain a contract; SOA keeps the data aligned. If you require this level of abstraction in the architecture, you should probably look at a SOA solution, rather than a microservices architecture.
Event sourcing is an architecture where you don’t store the current state of a model, but a series of events that have happened to the model. When you retrieve a model, you play back all the stored events and reapply them. This is called rehydrating an object.
Event sourcing is often combined with a Command and Query Responsibility Segregation pattern, because rehydrating an object can have a serious impact on performance.
A real-life example of event sourcing is accounting. When you add an expense, you don’t change the value of the total amount. Instead, a new line is added with the value and the operation. When an error is made, events should not be removed, as they actually happened in the past. To correct situations new events should be created to cancel out the erroneous event.
Advantages of event sourcing are:
- the design pattern provides a fully reliable audit log out of the box,
- it’s possible to implement queries that determine the state of an object at any point in time,
- since events are stored rather than domain objects, the Object-relational impedance mismatch is avoided.
Disadvantages of this architecture are:
- unfamiliar style of programming that has a (steep) learning curve,
- an event store is not always straightforward to query and can be rather difficult or complex. CQRS will most likely be used, which means that the application should be able to handle eventually consistent data.
Event sourcing should be used for applications that require a lot of traceability of changes. This can be the case for regular business data but will be more relevant for security-critical or sensitive data. Auditing or business analysis applications where you need to query the statistical data in all possible ways will also greatly benefit from this approach.
Picking the right software architecture for your business application is crucial for the success of the developers. It is the basis for communication, it’s the plan of the system and is primordial for the understanding between all stakeholders.
The architecture defines the model of the software, how it will function and define the problems you might encounter when it comes to implementation. It makes it much easier to take decisions and manage all sort of change and permits to get better estimates of the time and cost of a project.
Opinions expressed by Java Code Geeks contributors are their own.