Microservice architecture has been around for several years and is actively used within the software community. This architectural style evolved from Service Oriented Architecture (SOA), which has been around since the late 1990s and was created to provide an option to overcome the disadvantages found with a traditional monolithic architecture. Microservices architecture has been embraced by some of the largest global corporations, including Capital One, Amazon, Netflix, Walmart, and Uber.
Kikoda’s Business Analyst Joe Kalicki sat down with two Kikoda Senior Software Engineers, Robert Hodgen and Jeff Budnick, to discuss microservices and why there has been culture shift towards this architectural style.
Joe Kalicki (JK): Let’s begin with a description of what Microservices are.
Robert Hodgen (RH): Microservices is the continued evolution of applied best practices and networking resources. Computers used to be expensive systems, like mainframes, that companies would set up. Over time, computing power became cheaper, people start using rack space, and it became easier for people to program at home. This evolution continued to move steadily towards the cloud. Cloud deployments have a wonderful property of elasticity, so you can spin resources up on demand and kill them a minute later. Microservices are good for that.
JK: What do you think defines microservices?
Jeff Budnick (JB): The separation of concerns and reusable components is really what define a microservice. The idea that entities should have a single responsibility—and should be able to handle that responsibility really well—is not necessarily a new idea. This has been a cornerstone of computer science. Employing microservices is about taking those concepts and applying them to the web-based server world we live in. It’s making a single application that has a single purpose and an interface that’s easy to abstract and reuse.
JK: Can microservices be used in tandem with cloud services like Amazon Web Services (AWS)? If so, how can microservices benefit organizations?
JB: Organizations that have large monolithic environments find that a failure in one part of their processes can result in a cascading failure throughout the entire application. Microservices will eliminate the cascade effect found in monolithic environments. Microservices in tandem with a move to the cloud would reduce internal support costs and the costs of maintaining physical hardware. From the perspective of a business owner or a budget manager, using microservices in tandem with cloud services can be a very powerful tool with positive impacts to the budget.
JK: Tell me about some of the pitfalls for implementing microservices. One that comes to mind is that these services talk to each other over a network rather than through internal memory. Any advice or things to keep in mind when implementing microservices?
JB: There are two primary considerations that I always think about with microservices. The first, which I’m asked about frequently, is the lack of referential integrity between databases. Microservices are siloed. This can scare people who are used to larger applications that can connect to one large relational database. An example might be when information about a user—including the user id—is stored in a user service, but another service wants to know the user id of a person saving candidate data. In traditional relational database design, there’s a sort of physical relationship between the user id on the candidate table and the user id that appears in the user table. In a database, the two tables can be joined. When we implement microservices, a candidate service will only know about candidates, but it won’t know anything about users. The candidate service may know that there’s an associated user id, but the candidate service can’t join with the user service. However, there isn’t a need to worry about physical relationships any longer with microservices. With the advent of object-relational mappers (ORMs) like Entity Framework, all our business logic—which ensures that IDs are where they’re supposed to be, the length they’re supposed to be, and any other validation—happens in the app now.
Second is the issue of debugging across services. If you have a long-running service that interacts with multiple services, you must consider how they’re all interacting. One way is to make all the interactions asynchronous. It may add a small amount of overhead, but it will also ensure that each service is truly siloed. This way, if one service fails, it will not cause a failure in another service that may be attempting an interaction.
JK: How do you debug across service boundaries?
JB: Actually, you shouldn’t have to. Microservices should always act independently. Essentially what you have is an initiating process, either a command or a query, that either works or it doesn’t. There are event stores that will allow you to replay history and review the commands to see what happened. If you silo microservices correctly, they will work independently. For example, if we have a ticketing application, we know that at some point a ticket purchase will happen. When the ticket application starts to process a ticket, the independent accounting service will get notified by the ticketing service. Later, the accounting service will call back to the ticketing service to verify that the purchase was approved. In this example, you don’t have to worry about debugging across services. You recognize that you’re kicking off the “buy a ticket” call. In the ticket service, there’s a result that the payment for the ticket was completed. It’s a much more linear process from the perspective of the services, but from a design perspective, each service has its own independent list of things it is expected to do.
We typically think about an asynchronous process. When you call a class, traditionally, you don’t have to wonder whether it did something or not, because somewhere in the stack—at or below that class—an error will get thrown or it will return something. But with microservices, if one service fails, then no response will be returned from that service. The service making the call will either retry immediately or retry later.
RH: Regarding visibility or observability and debugging, there are many tools and approaches for tracing created to make sure you’re logging what you need and can visualize it in a good way. The other side of handling failures across service boundaries is thinking about the critical path and, as Jeff said, determining whether or not the system should retry a call or retry it at a later time. There are tools to handle that, like a Command Query Responsibility Segregation (CQRS) process.
JK: Is testing a microservices system easier than testing a monolith system?
JB: Oh yeah—if you’re working with a monolith system, a problem you routinely see is that it’s sort of like a big ball of spaghetti. You can tug on one side of the ball and you’ll hear noodles moving around on the other side, but you have no idea what’s happening inside the spaghetti ball. Did pulling on a noodle cause an unseen knot? That’s really rough on quality assurance testing because, even though a developer may change only one small thing, the testers have to rack their brains about what potential negative effects may occur as a result.
With Microservices you should be looking at single straight noodles. If a change was made in one service, then the changes and effects would be isolated within that one service. That’s all you should ever have to worry about.
RH: Thinking back to how we’ve implemented microservices on a certain project, we essentially never found problems deep down in the dependencies. Once all the dependencies have been coded, there isn’t a need to touch them again. Unless there is a need to alter user property or a query, we don’t have to go back and mess with dependencies. We have a high degree of confidence that they are good. We can go several weeks without changing those proxy services. We’re always changing the way we interact with the data, but not the way it gets pulled.
JK: Do you have parting words of advice for software engineers who are considering implementing microservices?
JB: I would learn more about Docker and AWS. That really helped to transform the environment for me.
RH: Work with a business analyst to learn the needs for the system. Legacy systems tend to have a big complex web of interactions. Mapping those interactions helped me design the service structure logically. You will end up uncovering additional constraints or gotchas when you do that deep dive with the assistance of a Business Analyst. Since it’s not a monolithic solution—where you can just tack another class on and patch it together and add to the pile of spaghetti—you want to define the lineup of those single strands ahead of time.
JK: Thank you, Jeff and Rob, for sitting down to discuss the reason cultural shift towards microservices.
If you have enjoyed this post and would like to learn more about Microservices see the list below.
Fowler, M., & Lewis, J. (2014, March 25). Microservices. Retrieved from https://martinfowler.com/articles/microservices.html#CharacteristicsOfAMicroserviceArchitecture
Fruhlinger, J. (2019, October 10). What are microservices? Your next software architecture. Retrieved from https://www.infoworld.com/article/3445043/what-are-microservices-your-next-software-architecture
Skok, D. (2016, July 4). Microservices Essentials for Executives: The Key to High Velocity Software Development. Retrieved from https://www.forentrepreneurs.com/microservices/
Lokesh.Rawat, A. (2018, October 8). Design Patterns for Microservices and Containers. Retrieved from https://devopedia.org/design-patterns-for-microservices-and-containers
Pandya, S. (2019, March 28). On Microservices and Containers – DZone Microservices. Retrieved from https://dzone.com/articles/of-microservices-amp-containers