Microservices design philosophies
Disclaimer: Views and opinions expressed in this article are those of the authors and do not necessarily reflect the official policy or position of my current or previous employers.
Microservices and UNIX
Today’s microservices have so much in common with the legendary UNIX philosophy. Here are some connections that I believe resonate well with what most of us are doing today, to that was created nearly 60 years ago.
There is nothing like great or worse architecture, sometimes a bad decision is better than being indecisive, how can one achieve greatness?
Software architects are entrusted to solve business problems, an architect creates an elegant solution domain to a business problem. The design principles an architect employs to a solution are influenced by their experience and situational parameters. As a key stakeholder of the system/software an architect is creating or taking care of, he/she has to make timely decisions to ensure various needs of actors are addressed while a robust system is built and operationalized. To achieve great architecture, the foundational principles on which these decisions are made are important and often critical. Anyone can learn a great deal by studying patterns and anti-patterns from various existing systems to elevate everything they build to leap toward greatness.
Here are some of the philosophies that I’ve borrowed over time from UNIX creators to build various systems at start-ups and a few fortune 500 companies.
Disclaimer: Observations and opinions stated in this article are my own, and do not reflect those of my employer or others. The comparisons might be a stretch from where UNIX originated, and I believe that some of the principles can be applied wherever necessary to benefit from it.
Philosophy #1
Small is beautiful,
Simple is elegant,
Do one thing, and do it well!
Build modular programs.
Philosophy #1 translates to loose coupling; Single Responsibility Principle (SRP): A module should be responsible to one, and only one actor.
Focus on service boundaries for a given business case, and eliminate tight coupling. Consumers should not be impacted by the changes done to the services.
UNIX Examples:
- cat
- date
- cal
Service Examples:
- UserService — Manages user data
- AuthenticationService — Authorize users
- PaymentService — Process payments for user invoice
- InvoiceService — Creates invoices for user purchases
Philosophy #2
Make it work together,
Use composition,
Separation of concerns!
Philosophy #2 translates to reuse, composability, and automaticity: Focusing on business boundaries allows canonicalizing models, thus paving ways for services to participate and collaborate to achieve a common business need.
UNIX Example:
- cat /etc/passwd | grep ironman | awk -F “:” ‘{print $5 }’
Service Examples:
- InvoiceService — Creates an invoice for one or more user transactions, and notifies invoice readiness actors.
- PaymentService — Processes payment for the given invoice and notifies actors (user and services)
Establish a contract such that the output generated by one program can be processed by another program even after the program implementation has changed. This allows new programs to do additional things business needs.
Philosophy #3
Least Surprise,
Repairability,
Discovery,
Transparency!
Philosophy #3 translates to fault tolerance: Each service by virtue is independent of the other, and has the opportunity to fail and recover. When one service fails, it must be noisy enough to make troubleshooting easy. Facilities are monitorable and internal states can be inspected. Has simple interfaces to allow easy manipulation.
UNIX Example:
- cat /dev/null — the cat program should be able to handle any input, in case it cannot fail early with enough information to debug.
Service Examples:
- PaymentService — Failure to process payment must be handled diligently and communicated to actors such that actors can retry or take corrective actions, data about why the failure occurred is very important.
- Administrators should be able to toggle log levels, inspect program states and evaluate root causes.
Philosophy #4
Robustness,
Extensibility,
Optimization!
Philosophy #4 translates to reuse: Close the code for modification, and open it for an extension. New capabilities are introduced by adding new APIs or by extending classes and setting up new endpoints. The prototype is first to get it working before optimizing; roll it out, test the waters and then make it robust. Abort investment in case you find something else already exists and solves your problem. Developer time is precious!
UNIX Example:
- shell scripts — use existing commands and build your tools.
Service Examples:
- PremiumSearchService — Provide quick turnaround and SLA for paid customers.
Summary
Greatness is a journey embraced with sacrifices, compromises, and adjustments. Make the foundation sturdy with solid principles and practices. Get, Set, and Evolve!