Last updated: 11.10.2021 04:53
I liked .NET technology from its inception. In fact I left the dark star of overxmlized J2EE development to join forces of rebellion around 2004. Over the years my team here at Altkom Software & Consulting built and maintained more and more complex business solutions for insurance and banking. While Java was in stagnation .NET platform developed very quickly. Several open source libraries were created and widely adopted like for example NHibernate, Castle Project or log4net. Things get even better with ASP.NET MVC. But somewhere around 2014 things changed as Microsoft started to heavily invest in .NET Core. The idea seemed great – have .NET running on all major platforms. But it took many years to achieve and first releases were a bit disappointing. So many missing APIs caused that many of our favourites libraries were not ported. First versions of Entity Framework lacked critical features. The future of .NET seemed doubtful. At the same time Java 8 came out and brought coolness back to the language, also microservice based approach to architecture emerged. This together with Spring Boot convinced me to move back to Java land.
But recent .NET releases – especially .NET Core 2.x and .NET standard initiative convinced me that .NET core is back in the business. So I with my teammates decided to explore possibilities and challenges of building microservice based solutions on .NET Core.
We have a lot of experience in the microservice area as we develop and deploy systems in this architectural approach since 2015. We wanted to find out possible options for dealing with typical microsevice related tasks like service discovery, service communication synchronous and asynchronous, data access to various data sources (relational and noSQL), logging, resilience, error handling, security with JWT and api gateway building.
In this series of articles we are going to walk through typical tasks required to build microservices based solution.
These tasks include:
Part 2: Shaping microservice internal architecture with CQRS and MediatR
Part 3: Service Discovery with Eureka
Part 4: Building API Gateways With Ocelot
Part 5: Marten An Ideal Repository For Your Domain Aggregates
Part 6: Real time server client communication with SignalR and RabbitMQ
Part 7: Transactional Outbox with RabbitMQ
The list will be updated after the publication of a new article in the series.
We are going to build very simplified system for insurance agents to sell various kind of insurance products.
Insurance agents will have to log in and system will present them list of products they can sell. Agents will be able to view products and find a product appropriate for their customers. Then they can create an offer and system will calculate a price based on provided parameters.
Finally agent will be able to confirm the sale by converting offer to policy and printing pdf certificate.
Portal will also give them ability to search and view offer and policies.
Portal will also have some basic social network features like chat for agents.
You can find architectural diagram of our system below.
The system consists of:
As you can see services are decomposed by business capability. We also have one technical service – document service. Introducing technical services makes sense when they require scalability/resilience and we want reduce time to update/fix or licence costs.
Each microservice is built around bounded context in DDD terms. Also you can observe that they cooperate to achieve the main business goal – to sell an insurance product to the end customer.
Below is a fragment of our solution open in Rider IDE. As you can see for each microservice we create three projects: one for API definition, one for implementation and one for tests. You can find source code for our project here on our GitHub. Note that it is work in progress and code will change as we progress with our series.
API definition project contains classes and interfaces that describe service capabilities exposed to the external world in form of commands it can handle, queries it can answer, events it emits and data transfer object classes it exposes. We can treat these as port definitions in a ports and adapters architecture.
Implementation project contains command handlers, query handlers and notification handlers which together provide service functionality. Most of the business logic goes into domain model part. Adapters for talking to external world are implemented as controllers (for handling incoming HTTP requests), listeners (for events delivered via queue) and REST Clients (for handling outgoing http requests).
Test project contains both unit tests and integration test.
In our opinion .NET Core is a great platform for building microservices and in this series of articles we would like to prove it. There is a rich ecosystem of open source tools and libraries around it. The C# language itself is a great tool.
The platform and libraries are all open source and new features are delivered at very high velocity.
Also .NET Core 2.x enhancements around performance and memory usage make it even more attractive.
In the coming articles we are going to present our findings and solutions for common tasks related to microservices implementation and deployment.
Author: Wojciech Suwała, Head Architect, ASC LAB[ratings]