Microservices are getting constantly more and more popular. Every month, there are more libraries and solutions supporting a development process, testing, and further support. Many Java developers have heard about the microservices approach, but a significant number of companies have not taken the challenge of implementing a microservices-based architecture. Does this sound familiar?
"I know, microservices are awesome, microservices bring easier maintenance and further development, but we have no resources to build a good microservices-based architecture."
In this article, let us give a try to go through the architecture of Abixen Platform and explain how this solution helps Java developers to start a microservices adventure. You can also find the platform on GitHub.
A lot of developers who have tried to create a microservices-based application (e.g. using the Netflix OSS stack) have wondered if they have sufficient knowledge to configure the whole system. The situation here is very different to monolithic applications, as the extra components come along with desirable functionalities, like request monitoring, a queue for sharing communication between services, a registry service, a configuration service, and many others. It's also common to have a good database, as it's a good practice to keep a separate data model for each microservice.
When creating a modularized web application, developers have to be focused on a well-made domain design. Minimizing all links between modules to a minimum avoids a complex architecture.
Microservices Architecture in the Abixen Platform
In the diagram below, you can see the key application elements of the Abixen Platform.
Click to enlarge
In the diagram, we can find:
Eureka: works as a registry service.
Hystrix Dashboard: allows us to monitor request status in real time (e.g. how many requests time out, how many are successful, how many fail, and so on).
Zipkin: a distributed tracing system that helps gather timing data needed to troubleshoot latency problems in the application.
Redis database: used for keeping information about signed-in users.
RabbitMQ: used as a queue for sending messages between particular microservices. For example, in a case when the core microservice must remove a module instance and let the Business Intelligence Service or Web Content Service remove all configurations related to it.
Abixen Platform Common: a JAR containing a common API that can be implemented using a new functional microservice.
Abixen Platform Configuration: a configuration microservice. Here, an entire application can keep configs for each profile in YAML files.
Abixen Platform Web Client: static content of the core functionality, like page and module management, security management, and others.
Abixen Platform Gateway: plays the role of a security gateway. Each request must go through this microservice.
Abixen Platform Core: has its own core database and holds the core functionalities of the platform, like page and module management, security management, etc.
Abixen Platform Business Intelligence Service: Since the application brings functional microservices, here is where we place a module for business intelligence reporting, chart creation, and further management. It also has its own database.
Abixen Platform Web Content Service: This is similar to the above service, but this one brings content creation functionalities — e.g. a user is able to create articles. It also has its own database.
Your Services: a place designated for custom microservices created by developers using the platform.
The architecture is based on the Netflix OSS stack. The functional microservices are not required to properly work through the whole platform. If you want to use the platform, but say you don't need to report charts or an article manager, you can deploy the platform and use solely independently developed microservices.
The architecture is well-mapped to Amazon components as well. An application can be deployed on AWS using services like EC2, ALB, ECS, ECR, Route53, CloudWatch, Elasticache, ERD, and SES.
How Does It Work?
A user generates a request using a Web Client. Through Zuul Proxy, the request is forwarded to the Gateway. Now, the Gateway microservice performs security operations. If the user is not authenticated, it throws an "Unauthorized" exception. If it is authenticated, then Zuul Proxy decides whether the request should be sent to Core or a particular functional microservice, like Business Intelligence, Web Content, or one created by third-party developers. In the listing below, we can see a sample configuration of the routing:
zuul: host: connect-timeout-millis: 10000 socket-timeout-millis: 60000 routes: # Begin of custom module microservices - add mapping relevant to your microservice # Begin of Business Intelligence microservice businessIntelligenceApplication: path: /service/abixen/business-intelligence/application/** url: http://business-intelligence-service:9091/service/abixen/business-intelligence/application sensitive-headers: businessIntelligenceApplicationApi: path: /api/service/abixen/business-intelligence/application/** url: http://business-intelligence-service:9091/api/service/abixen/business-intelligence/application sensitive-headers: businessIntelligenceAdmin: path: /service/abixen/business-intelligence/control-panel/** url: http://business-intelligence-service:9091/service/abixen/business-intelligence/control-panel sensitive-headers: businessIntelligenceAdminApi: path: /api/service/abixen/business-intelligence/control-panel/** url: http://business-intelligence-service:9091/api/service/abixen/business-intelligence/control-panel sensitive-headers: # End of Business Intelligence microservice # Begin of Web Content microservice webContentApplication: path: /service/abixen/web-content/application/** url: http://web-content-service:9092/service/abixen/web-content/application sensitive-headers: webContentApplicationApi: path: /api/service/abixen/web-content/application/** url: http://web-content-service:9092/api/service/abixen/web-content/application sensitive-headers: webContentAdmin: path: /service/abixen/web-content/control-panel/** url: http://web-content-service:9092/service/abixen/web-content/control-panel sensitive-headers: webContentAdminApi: path: /api/service/abixen/web-content/control-panel/** url: http://web-content-service:9092/api/service/abixen/web-content/control-panel sensitive-headers: # End of Web Content microservice # End of custom module microservices resource: path: /resource/** url: http://core:9000 sensitive-headers: api: path: /api/** url: http://core:9000/api sensitive-headers:
Either a particular functional microservice or Core serves content adequate to the request. Core supports only a rest. Despite this, the functional microservices can handle requests for both a rest and a static content. The mentioned components support an authorization process — that is why they are able to throw a Forbidden exception.
Developers who want to create their own functional microservice can leverage a generic API, where they can find both abstracts and interfaces. The service must obtain a deployment descriptor. This file describes what modules a developer included inside, what static resources are available, and if there is any configuration panel in a control panel (because there is an opportunity for further configuration in the control panel). In the listing below, we cover a sample deployment descriptor of a microservice:
The Core microservice uses the descriptor to get information about the functional microservice and, based on this, injects all the delivered features to the platform.
As we can see, Java developers do not have to create a whole microservices architecture from scratch. If it suits your needs, we've covered a ready-made solution and started the development of functional microservices. Of course, there may be situations where modifications of areas other than one's own microservices will be required, but still, the architecture exists.