A SAGA microservice application - 7

Ok, if you have somehow stopped by this post, you have 2 options depending on your knowledge. If you are an experienced developer with microservices and spring cloud experience, you can fork the project from github and download config repository and try to run the whole system according to this post. If you are not an experienced developer, you should start with the first post of the series. In this post, i will analyze the microservice project that i have built and mention couple of different alternative tools or approaches. I was going to introduce these tools but it would make this post way too long. So it will be in another post. I will ask questions like "what did we do", "why did we do it", "did we do it right" and "what did we gain or sacrifice" kind of questions. The aforementioned microservice architecture that i have coded is represented in the image below.

What did we do

We have created a ticketing app that runs on microservices. It has a Gateway, Discovery Server, Config Server as the backbone of the system. We have connected the small services to Eureka with load balancing. We have routed the requests with Gateway to Eureka and provided resilience with fallback routes. Then we have coded small services and failovers with REST endpoints that has no involvement with business side. We have designed functions for users, activities and the payment processes as small services by grouping the relevant methods together.

After this implementations, we have coded a consumer web MVC app and implemented the SAGAs for business flows. It reaches to small services with JWT headers for security via the gateway. MVC app also coordinates the service calls and manages exceptions according to the responses. Config Server helped us to store the spring boot configurations in a github repository and serve them to services. The databases i have used was MySql but it wasn't a vital point of the microservice. All of this is sponsored by spring cloud :)

We could have started with domain driven design and define bounded contexts. I have kind of skipped that part by dictating my own decisions. I think in this design, a user, an activity and a payment process can be bounded contexts because everyone can agree on what they are. But i might be wrong, i would be glad to hear your thoughts about how to implement domain driven design for this architecture.

We used a pattern called gateway pattern i guess but when it comes to microservices, that is just a name. Because we have also used a SAGA pattern and circuit breaker pattern. If you define a web service with patterns, i am 100% sure everyone will draw a different image in their mind. That is why i have never mentioned any pattern during the series. You should be aware of them of course, but, deciding which one to use is going to determine how long your microservice will be able to live and prosper. So the question of WHY is important. Let 's take a look at my reasons.

Reasons

Every choice you have made must have a reason. It better be a logical or practical one, rather than "i knew this way" or "they told me this way". I have made several decisions about technologies and tools and the whole architecture.

  • I have used spring cloud technologies because i knew this way :) Yes you can laugh now. There are other options for these tools but gateway helped me create routes with Java code and Eureka was the easiest option to provide service discovery and registery.
  • I have considered the business flows as SAGAs since they are all individual stories with a beginning and ending. Yes i know SAGAs are mainly about data consistency and there are tools to manage SAGAs but i wouldn't limit it to just consistent DB transactions.
  • I have decided that consistency is more important than resilience. You can wait for couple of hours to buy an event ticket. Also i have decided that this website will not get 10K clicks every seconds, this isn't netflix.
  • I have decided to implement a coordinator on top instead of a message based system. Because i thought a gateway and a load balancer would be enough to manage this application. Heavy activity was not the main concern.
  • I have implemented the business flows with coordination so that i can easily develop them. Otherwise a message based approach would be too hard to implement. (More on coordinator VS message queue at the end)
  • I have used JWT for security because some operations require a logged in user, some don't. I didn't have much security concerns and thought token based security is the best practice for Restful services.
  • I have considered 3 fault tolerance aspects. Business faults, exceptions and network faults. Network faults are handled by resilience4J. The first 2 are inside the MVC app with stories, in other word SAGAs.
  • Most importantly, i have decided to build a microservice becase i wanted to have an actual running implementation :) Was it mandatory to go for microservices instead of a monolithic app? Maybe yes, maybe not.

These were the reasons that i can remember to the most crucial decisions i have made throughout this journey. I have created this system with zero hands-on experience and some implementations were changed along the way. But in the end, this is what caused the image you see above. My reasoning can be wrong, all of these can be argued. There could be better answers to these concerns. But you have to be able to answer the question of why. After all, you are aiming to have some sort of gaining from these decisions. It brings the next question.

Gainings

I think i have gained something with the decisions i have made.

  • It is easy to add whole bunch of services to the system. This is actually a benefit of microservices. Also, i have a guide to how to add a new service for juniors.
  • All of the small services can connect to a different DB technology and can be implemented with a different language. This is also a benefit of microservices.
  • It will be easy to debug the code, since i have written coordinators inside MVC app. I didn't need a distributed tracing solution. Also, adding a new business requirement or a new service is fairly easy. Since it is just a small service and some business code.
  • I don't have spaghetti relationship among the services. (Some people avoid spaghetti code and ends up with spaghetti services)
  • My small services are micro, because they don't have business flows inside. Otherwise it could be macroservices instead of microservices.
  • The util project made it easy to transport data from services to MVC app by providng constants or custom POJOs.
  • Config server reduced some boilerplate configuration.
  • Gateway and Eureka is doing mostly basic operations. I don't think they will have much trouble under heavy load.
  • You can always divide services into even smaller services if they get heavy. Your business needs may change and services can adapt.
  • I have assured resilience with the gateway using Resilience4J
  • I guess the most important one is that you can individually develop and deploy a service without stopping and deploying the whole system.

If i am not fooling myself these are the advantages of my system. I could be short sided here. Remember we had requirements, limitations and goals. Just because we have achieved our goals, doesn't mean we have covered all possible problems and requirements. It also doesn't mean that we did the right thing or solved the problems the right way. Roast me in the comments :) If i am honest about the system, i should also mention the disadvantages of the system as far as i can see. This part usually becomes clear after putting your microservice into use.

Disadvantages

  • If the business requirements get more and more complex, you might end up a monolithic looking MVC app.
  • This system takes all the load from the services and puts it on MVC app. It could get computationally heavy over time. (You would need another load balancing solution behind the MVC app and multiple instances of it.)
  • Gateway and Eureka could be crushed under heavy traffic. We don't know their capabilities. If they are not able to manage the load, you have to insert a new gateway and eureka to load balance among gateways.
  • Gateway, Eureka and Cloud Config are working single instance. What happens if one of them goes down? I am too lazy to try it out.

Well it looks like i have created the most awesome microservice implementation ever. Again, i could be short sided here because my ticketing app is imaginary and i might have a biased view. A second opinion from out of the system can get you a new perspective. Furthermore i can never honestly answer the question "did i do it right". I have met the requirements, implemented solutions according to my criterias and limitations. I would say i did it right, if i have achieved the expected results. But microservices is an abstract and vague concept. There are trade offs instead of de factos. If you are still not convinced that i have mastered the microservices, you are right :) I have questions that even i can't answer.

Questions

I would be glad to hear your thought about these questions in the comments. Most of these are the results of "did i do it right" questions.

  • Could it be Eureka first and gateway second? So that i can load balance among gateways? Would it make sense?
  • Since the small services has no interaction among them, is it necessary to apply distributed tracing with a tool like Sleuth and Zipkin brothers?
  • Did i apply DDD? If not, how could it be implemented?
  • Is the data transfer between MVC and services encrypted? Probably not. I guess you need SSL kind of thing there.
  • Is there a third way? A coordinator based MVC app and Choreography among the services and a third kind?

Ok, i am technically done with the microservice. The jury heard me out and now i have a working project. But i am always tended to be a dreamer or philosopher. I have 2 "what if" questions and solutions in my mind, as alternative solutions. These are the conclusions i have made with my experience.

What if SAGAs become too big

There is a possibility that in time, these business operations and transactions may become way too complex and big. I think that would cause 2 things: Longer response times and huge controller or service methods. In this case, i would stick with my own design because i want the services to be small and free. I could try modularizing MVC app with maven modules according to SAGAs, so that i can separate the code a little bit. Also design patterns are helpful to write understandable code. You are basically implementing some sort of Visitor pattern inside the MVC app.

I can't help with response times since no matter what i do, huge business flows will cause lots of service calls. But if they get computationally heavy on the MVC app, i guess load balancing before MVC app would be what i do. It is all to prevent spaghetti relationship among the services with message queue approach.

But no matter what i do, i think there is always a choreograpghy based microservice architecture that can be a solution for some situations. I might implement an example of it one day. I have one more question already.

What if there is too much user activity (Choreography)

In this case i may not need a gateway and eureka. As far as i know, i can put a message queue (kafka / rabbitmq / activemq) right in the center of the system like a network hub. I can also make every service a message listener that listens to these queues by subscribing. MVC app starts a message and that queue is listened by the first service. It receives the payload and does its own thing and puts it to the next relevant queue according to business flow or the information inside the payload. This is a mixture of Chain of Responsibility and Mediator design patterns.

This would create lots of queues for all sorts of business flows, which i don't see logical. I would also desperately need Sleuth + Zipkin (or any other tool) for distributed tracing. MVC app would also wait for these queues for an answer and this would dictate a rective framework to use. This is how i can see choreography working.

Final thoughts

At the end of this journey, one thing is for sure. All of the decisions, tools and implementations can be questioned or critisized. I have learned and developed some kind of microservice solution and met the tools. I don't see any solution bewteen coordinator and message queue implementations. Here is my takeaway from all this:

  • Resilience + independent modules makes a microservice viable
  • It provides tehnological indepencence for components
  • Coding is easy, you have to analyze the domain and the expectations thoroughly
  • You should get to know the tools and alternatives before using them

With this post i have completed my goal to have a running Spring Cloud microservice. Congratulate yourself if you have done the same thing with zero experience. After this point, there comes the management of the microservices. You see, we have to manually run and deploy the services everytime we change things. That is why "continuous integration" and "continuous deployment" comes into play. This would take you to another journey called Devops. I will briefly write about it some day. This series is done. It took 2 months to write the code and 7 blog posts. See you at the next post :)


Leave a comment