1

I have been assigned to think of a layered microservices architecture for Azure Service Fabric. But my experience mostly been on monolithic architectures I can't come up with a specific solution.

What I have thought as of now is like...

Data Layer - This is where all the Code First entities resides along with DBContext.

Business Layer - This is where all the Service Managers would be performing and enforcing the Business Logic i.e. UserManager (IUserManager), OrderManager (IOrderManager), InvoiceManager (IInvoiceManager) etc.

WebAPI (Self Hoted Inside Service Fabric) - Although this WebAPI is inside Service Fabric but does nothing except to receive the request and call respective Services under Service Fabric. WebAPI Layer would also do any Authentication and Authorization (ASP.NET Identity) before passing on the call to other services.

Service Fabric Services - UserService, OrderService, InvoiceService. These services are invoked from WebAPI Layer and DI the Business Layer (IUserManager, IOrderManager, IInvoiceManager) to perform it's operation.

Do you think this is okay to proceed with?

One theoretical issue though, while reading up for several microservices architecture resources, I found that, all of them suggests to have Business Logic inside the service so that the specific service can be scaled independently. So I believe, I'm violating the basic aspect of microservices.

I'm doing this because, the customer requirement is to use this Business Layer across several projects, such as Batch Jobs (Azure Web Jobs), Backend Dashboard for Internal Employees (ASP.NET MVC) etc. So If I don't keep the Business Layer same, I have to write the same Business Logic again for Web Jobs and Backend Dashboard which I feel is not a good idea. As a simple change in Business Logic would require change in code at several places then.

One more concern is, in that case, I have to go with Service to Service communication for ACID transactions. Such as, while creating an Order, a Order and Invoice both must be created. So in that case, I thought of using Event Driven programming i.e. Order Service will emit an event which the Invoice Service can subscribe to, to create Invoice on creation of Order. But the complications are if the Invoice Service fails to create invoice, it can either keep trying do that infinitely (which is a bad idea I think), or emit another event to Order Service to subscribe and roll back the order. There can be lots of confusion with this.

Also, I must mention that, we are using a Single Database as of now.

So my questions are...

  1. What issue do you see with my approach? Is it okay?
  2. If not, please suggest me a better approach. You can guide me to some resources for implementation details or conceptual details too.

NOTE : The requirement of client is, they can scale specific module in need. Such as, UserService might not be used much as there won't be many signups daily or change in User Profile, but OrderService can be scaled along as there can be lots of Orders coming in daily.

I'll be glad to learn. As this is my first chance of getting my hands on designing a microservices architecture.

1

This is a pretty broad - but good - question, so rather than giving a single answer I'll deal with some of your statements in the hope that it will offer some guidance.

One theoretical issue though, while reading up for several microservices architecture resources, I found that, all of them suggests to have Business Logic inside the service so that the specific service can be scaled independently. So I believe, I'm violating the basic aspect of microservices.

  • That is correct; the entire point of a microservice is that it is a self-contained service that does one particular thing (whatever that is) without having to rely on any other services. The reason for that is not just to be able to scale parts of your system independently, but also to make it easier to modify parts of your system in isolation from the others (in addition to introducing fault tolerance, etc)

I'm doing this because, the customer requirement is to use this Business Layer across several projects, such as Batch Jobs (Azure Web Jobs), Backend Dashboard for Internal Employees (ASP.NET MVC) etc. So If I don't keep the Business Layer same, I have to write the same Business Logic again for Web Jobs and Backend Dashboard which I feel is not a good idea. As a simple change in Business Logic would require change in code at several places then.

  • The trick here is to design your microservices correctly so that those other applications can re-use them. So you have a single service that performs a function and therefore a single code-base to update, however consumers of this service (i.e those other applications you mentioned) do have to communicate over the network which is perhaps a potential problem performance-wise but shouldn't really be a deal-breaker. For example you could have instances of a microservice that the back-end dashboard uses that are only used by that application and are setup on the same network segment, etc.

  • If re-using the services in this way is really a no-go then you can have the next best thing which is to package the business objects, etc, into nuget packages and let these packages be dependencies of both the services and the other applications. Personally I do not like this way as it needs to be very carefully managed to make sure everyone upgrades at the same time but it is possible to make it work if you are careful.

One more concern is, in that case, I have to go with Service to Service communication for ACID transactions. Such as, while creating an Order, a Order and Invoice both must be created. So in that case, I thought of using Event Driven programming i.e. Order Service will emit an event which the Invoice Service can subscribe to, to create Invoice on creation of Order. But the complications are if the Invoice Service fails to create invoice, it can either keep trying do that infinitely (which is a bad idea I think), or emit another event to Order Service to subscribe and roll back the order. There can be lots of confusion with this.

  • Microservices in general shouldn't be talking to each other directly, because that's a sign that they are functionally coupled and therefore belong together as a single, larger service. In your example, firstly I would be surprised that you would cancel an order just because you couldn't also create the invoice at the same time, but then again it's your business rules, not mine :)

  • That's where a Gateway comes into play; this acts like an "Orchestrator" for some kind of business process like

    1. Tell the Order Service to create an order
    2. If that works, tell the Invoice service to create an invoice
    3. If #2 fails, tell the Order service to cancel the order and return failure to the caller
    4. If #2 succeeds, return success to the caller
  • This is a relatively simple way to manage the most common kind of issues you will get with distributed transactions (but by no means can it handle all of them easily because that's just the nature of distributed transactions)

  • This is very much like the style of development you would do if your OrderService and InvoiceService were statically linked assembles in your app, the only difference is that the calls are now over HTTP instead of local binary. That's one of the reasons this is a very easy style to develop in and maintain.

  • The alternative is a purely "Choreographed" system where the client is calling the Order service directly, which in turn sends a message which is consumed by the invoice service, and so on. This is very similar to what you have suggested but handling rollbacks becomes much harder.

    • Your best bet there is to have a 3rd service which receives failure messages from the Invoice service and is able to then tell the order service to roll back a specific order...in essence, it's like the Gateway approach except backwards. It's great for systems where you specifically don't have distributed transactions though, and where you are happy for everything to happen asynchronously.
    • Bear in mind that in this kind of system it can be very hard to understand what the business process being executed is, because everything is occurring in seeming isolation so there can be maintenance overheads with this approach as well.

    • Over time with the Orchestration approach you will find opportunities to introduce some Choreography - for example, your EmailService probably doesn't really need to be part of any business process or enrolled in any transactions, it just needs to receive a message saying "Send this email to so-and-so" and it doesn't need any additional context. So what will happen is that you will start to split out parts of the system that can do this and you will benefit from both approaches over time.

  • I generally recommend the first approach because it's nearly always "good enough" and is a lot easier to develop, so try designing it with an Orchestrating gateway in mind and you can't go far wrong.

p.s. the "single database" issue doesn't need to be an issue, as long as you are willing to have multiple schemas (i.e. multiple copies of specific service's "views" of the data they need to use). Otherwise it will be a bottleneck if you use a fully shared schema.

Hope this helps!

|improve this answer|||||
0

You can reach the main advantages of microservice architecture if every microservice will provide his own layers (Data layer, Business layer, etc) and is the owner of a part of the database. You could define different database schema and still use one database. For migration purpose, you could still use your existing layers. The design patterns Strangler and Sidecar can also be suitable for you. In my opinion, new features should be put in own assemblies of the service.

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.