Carving it up – Microservices, Monoliths, & Conway’s Law

Felling a gumtree

In a previous post, I quoted a passage from Ruth Malan’s “Conway’s Law”:

The organizational divides are going to drive the true seams in the system.

The architecture of the system gets cemented in the forms of the teams that develop it. The system decomposition drives team ownership. Then the organizational lines of communication become reflected in the interfaces, with cleaner, better preserved interfaces along the lines where organizational distance increases. In small, co-located teams, short-cuts can be taken to optimize within the team. But each short-cut that introduces a dependency is like rebar in concrete–structurally efficient, but rigid. If the environment changes, demanding new lines to be drawn, the cost becomes clear. The architecture is hard to adapt.

Those eight sentences contain extremely important considerations for application architecture, particularly if you are considering a microservice architecture.

Microservice architectures, although they have gained currency due to a recent post by James Lewis and Martin Fowler, are not a new concept. Lewis and Fowler note a pedigree going back as far as 2011, and in the opinion of Steve Jones, “Microservices is SOA, for those who know what SOA is”. Roger Sessions published a 3 part series in the fall of 2012 detailing his concept of a “Snowman Architecture” (Part 1, Part 2, Part 3), which is fundamentally similar to the Lewis and Fowler model.

According to Lewis and Fowler, the characteristics of a microservice architecture are:

  • Componentization via Services: Services are independently deployable, encapsulated, out-of-process components that can be composed into a system of systems with their interactions defined via service contracts (APIs).
  • Organized around Business Capabilities: Services are vertical slices of technologies dedicated to particular business capability, ideally with coinciding service and team boundaries.
  • Products not Projects: The team should own the service over its entire lifecycle, both development and support. (*)
  • Smart endpoints and dumb pipes: Logic should reside within the services rather than the communications mechanisms that tie them together (such as an ESB).
  • Decentralized Governance: Because of the partitioning inherent in this style, teams are not forced into one set of standards for their internal implementations. (*)
  • Decentralized Data Management: Because of the partitioning inherent in this style, data will be distributed across multiple services with a tendency towards eventual consistency.
  • Infrastructure Automation: Lewis and Fowler recommend automated testing, automated deployment and automation of infrastructure. (*)
  • Design for failure: The distributed, partitioned nature of microservice architectures increases the need for system monitoring and resilience.
  • Evolutionary Design: Smaller, modular services enable agile, controllable change.

* Note: I consider these to more in the realm of process than architectural style. That being said, I also consider most of these to be good practice for teams using this particular architectural style.

Composing a system of independent, autonomous components, as opposed to a monolith, allows them to be partitioned by business capability as well as be developed and maintained by a permanent team. This transforms Conway’s Law from an observation about the nature of software systems into a principle that can be used to improve the structure of those systems (“The organizational divides are going to drive the true seams in the system.”). The social system architecture will influence the software system architecture in any event, the question is whether that influence will be intentional or not.

The microservice architectural style has been described as “applying many of the principles of SOLID at an architectural level”. As opposed to monolithic architectures, it enables a high degree of flexibility, cohesion and composability (when done well). It is not, however, a free lunch. Distributed applications are more complex than monolithic ones in terms of development, operations, and governance. Accessing components that are out-of-process, not to mention over the network, is not the same as dealing with in-process components. Network latency becomes more and more important as applications become more distributed. Asynchronous operations can ease some of that pain, but at the cost increasing the complexity of the system.

Overly granular services can be another pitfall with this architectural style. In “Services, Microservices, Nanoservices – oh my!”, Arnon Rotem-Gal-Oz discusses the nanoservice anti-pattern where “…overhead (communications, maintenance, and so on) outweighs its utility”. To expand on his “so on”, security is one of those overhead aspects that can become more burdensome as the application becomes more distributed. At the very minimum, authorization, if the application is entirely internal, is more complicated than it would be with a monolith. External users and/or dependencies increase the scope of that security overhead, potentially adding multiple methods of authentication, encryption, etc.

Data architecture complexity is another consideration. It should be borne in mind that each microservice is its own self-contained system with its own data store(s). This adds fragmentation and most likely, redundancy, to the enterprise’s data architecture. Redundancy is not bad per se (e.g. caching), but it does require more in terms of governance to identify what source is authoritative and what others are not. Likewise, fragmentation is not always poor design (think bounded contexts with shared concepts), but it may require more effort to consolidate all the data for a given entity for reporting purposes. One might be tempted to “cheat” and go the shared database route, but that would be a mistake.

In a post on application boundaries, Martin Fowler observed that “essentially applications are social constructions…drawn primarily by human inter-relationships and politics rather than technical and functional considerations”. Realizing that, we can use the organizational dynamics to shape the components of our systems of systems in a way that helps, rather than harms the technical and functional considerations. The critical things to keep in mind are balance and intent.

9 thoughts on “Carving it up – Microservices, Monoliths, & Conway’s Law

  1. Your argument can also be leveraged when considering process improvements across team and organizational boundaries.

    Application architectures and organizational boundaries are interrelated and covariant.

    Like

  2. I would add a warning here. One of the reasons that micro-services can lead to excessively distributed out-of-process components with unnecessary high overhead is because when developers here the term “service” they automatically start implementing a web-service even when the same architectural goals can be gained by using a library that is compiled in-process and requires no cross-network overhead. Developers need to ask themselves, “Will this service really be used by multiple projects and does it really need to be implemented as web-service that would be accessed from many different applications simultaneously?” If so, let’s implement a web-service. If not, my service API could just as well be a set of library function calls, built and maintained separately from my application but compiled and executed within the application.

    Agile principles tell us to implement the minimal architecture that satisfies the need, and only go to a more complex architecture when the need outgrows the minimal implementation.

    Like

    • My dividing line between external library and service depends first on whether or not the external component has its own data associated with it. I don’t like to use the word “inevitably”, but when talking about shared databases and trouble, I’m tempted. If it’s pure function, then as a general rule I’d say go with a library, although there are cases where it’s useful to go out of process for asynch execution or to share expensive dependencies. Beyond that, I’m less worried about reuse and more concerned with partitioning and amenability to substitution. The trick is avoid going overboard.

      Like

  3. Pingback: More on Microservices – Boundaries, Governance, Reuse & Complexity | Form Follows Function

  4. Pingback: Microservices and Data Architecture – Who Owns What Data? | Form Follows Function

  5. Pingback: Fears for Tiers – Do You Need a Service Layer? | Form Follows Function

  6. Pingback: Making and Taming Monoliths | Form Follows Function

  7. Pingback: Microservices – The Too Good to be True Parts | Form Follows Function

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.