Back to all Articles
How to Create Microservices Architecture - Our Experience
Why Have We Decided to Write About Microservices?
While numerous articles discuss microservices architecture, we decided to share our hands-on experience implementing microservices framework for our customer - BOWWE.com
BOWWE is a very advanced and easy-to-use website builder with plenty of different functionalities that help businesses succeed online. The product is very complex due to many additional features like, e.g., booking engine, portfolio engine, drag and drop website builder. Building all those features in monolithic application architecture or in an application as a set of independent components could be very difficult.
So we decided to implement microservice architecture from the very beginning. Using microservices permits us to work simultaneously on several different features, speed up development, and cut delivery time. Read about our journey in building a successful microservices architecture.
What Is Microservices Architecture?
This design pattern improves scalability, performance, and development speed, making it ideal for large, complex applications. It also allows an organization to evolve its technology stack. However, we need to be conscious that microservices application creates additional problems with, e.g., maintenance, and debugging.
The Microservices Architecture Patterns Step-by-Step
1. Decomposition approach
Available decomposition approaches:
- Decompose by business capability
- Decompose by subdomain
- Self-contained Service
- Service per team
Our approach:
We decomposed our service using decomposition by subdomain, which means that each subdomain corresponds to a different part of the business.
We tried to avoid splitting the business relation set of functions between other services and making our developers easy to understand and manage. This way, services are loosely coupled.
E.g., our architecture embraces the following list of microservices:
- Pages MS
- Domains and Users MS
- Products MS
- Orders MS
- Product Deliveries MS
- Payments MS
- Integrations and mapping MS
- SSR WebPage rendering MS
- Statistics MS
Definitely, in this approach, you need to understand your business very well to decouple architecture properly.
We thought about a Self-contained Service approach, but they are much harder to maintain in splitting business logic and responsibility. Same functions may be implemented in different services, which we want to avoid as much as possible.
An example of the structure of that service you can see in picture:
2. Data management
Available Data management approaches:
- Database per Service
- Shared database
- Saga
- API Composition
- CQRS
- Domain event
- Event sourcing
Our approach:
We chose a database per service approach, but we have one service which has a shared database. It was decomposed from other MS and is not temporal because there were many data dependencies. Removing those dependencies would take too much time. So in the future, we are planning to do it, but there are another significant priorities at the moment.
In BOWWE, each MS has its own database, which is either MySQL, Cassandra, or Redis. We also use Redis as a distributed cache.
Such structures complicate situations when specific tasks demand access to two different databases owned by two other microservices. Sometimes tasks need to change multiple different databases to execute tasks.
In BOWWE, we have had such a type of problem, especially in ‘Orders MS’, which requires communication between products, payments, and delivery microservices.
We considered the CQRS approach, but for this time, it was additional effort and cost for us, so we decided to leave it as an option in the future in case of performance issues.
An example of that databases structure you can see in picture above.
3. Testing
Service Component Test - we use the service Component Test by mocking external service calls. This approach is easier and cheaper. It doesn't require a cross-service test environment setup, but we can have some integration problems that our tests won't catch.
Consumer-driven Contract Test - we are using those kinds of tests to test integration with external services and ensure that their contract has not been changed. So, in that case, we are doing a Consumer-side contract test at the same time.
We are also using cypress for end-to-end and visual regression tests, which allows us to decrease the number of manual testing before each release and gives us better confidence that the functionality we release is working fine.
4. Deployment
Available Deployment patterns:
- Multiple service instances per host
- Service instance per host
- Service instance per VM
- Service instance per Container
- Serverless deployment
- Service deployment platform
Our approach:
We are using the approach of having multiple services per host. Our services are running on dedicated servers. This is more cost-effective for us in comparison with hiring cloud DevOps infrastructure.
We are planning to run each service in a separate container. It will make our services more scalable and easier to configure.
An example of that approach you can see in picture:
5. Communication styles
Available Communication styles:
- Remote Procedure Invocation
- Messaging
- Domain-specific protocol
- Idempotent Consumer
Our approach:
For communication, we are using RestAPI (an API gateway) and Apache Thrift. Apache Thrift is a framework with a cross-language code generator that helps us build faster communication between clients and services regardless of the used development languages.
In BOWWE, Apache Thrift is used for communication between services responsible for getting page data. We choose Apache Thrift because of better latency compared to Rest API and programming language independence.
Apache Thrift supports most of the languages, and our core services are written in Java and PHP. We also use REST APIs where latency is not so important and for our public API.
An example of that RestAPI communication structure you can see in picture:
6. Observability
Available Observability solutions:
- Log aggregation
- Application metrics
- Audit logging
- Distributed tracing
- Exception tracking
- Health check API
- Log deployments and changes
Our approach:
None of the above.
Each service instance generates its logs and writes to a log file in a standardized format. The log files contain errors, warnings, information, and debug messages. So far, this approach is good enough for us. In the future, we may start to use dedicated tools like, e.g., Splunk, Logstash, or Appdynamics.
Regarding observability, we can add that BOWWE possesses a dedicated microservice that delivers deep analytics about traffic and customer behavior to the owners of the websites.
Tech stack of that microservice: Spring Boot uses the NoSql Cassandra database. In the future, we are thinking about introducing Apache Spark.
Key Takeaways from Building Microservices for BOWWE
Pros and Cons of Microservices Architecture
Like every architecture, the microservice architecture has its pros and cons, especially when compared with a typical, most popular, startup-loved monolithic architecture. Below are all the pros and cons regarding microservices architecture:
- Services can use different technologies and test environments.
- Changes in one service don't impact others.
- Modern containerization tools like Kubernetes enable precise resource allocation for each service.
- Updates and fixes can be deployed without taking the entire application offline.
- Designing the microservice architecture, selecting communication protocols, and defining database schemas require extensive planning.
- Separate teams for each service increase operational expenses.
- Distributed systems demand advanced tools like OpenTracing to replicate conditions and trace issues.
- Logs must be aggregated and analyzed to maintain observability across services.
Final Thoughts
From our experience, building microservices architecture from the very beginning of the project was an excellent decision. Definitely, in the beginning, we have spent a higher number of working hours on the architecture design of the solution, but later it was much easier to develop each functionality separately and manage a few development teams simultaneously.
At this point in the project life, adding such significant parts of code like e-commerce creator and later in the future web app builder would be impossible without this architecture.
If you're interested in learning more about this project or to implement microservices architecture in your web application or other software development porject, feel free to reach out to us. We’re happy to share insights and help you build similar solutions!
Why have we decided to write about microservices?
What is microservices architecture?
The microservices architecture patterns step-by-step
Key takeaways from building microservices for BOWWE
Pros and Cons of microservices architecture
Final thoughts