Microservices

architecture

Microservices

Definition

Microservices is an architectural style in which an application is structured as a collection of small, loosely coupled, independently deployable services, each implementing a single business capability, owning its data store, and communicating with other services through well-defined APIs (typically HTTP/REST, gRPC, or asynchronous messaging). Each service can be developed, tested, deployed, scaled, and maintained independently by a small team, enabling organizational scalability where multiple teams work on different services in parallel without tight coordination.

The microservices architecture emerged as a response to the limitations of monolithic applications, where all functionality resides in a single deployable unit. In a monolith, changes to any component require rebuilding and redeploying the entire application, scaling requires replicating the entire application even when only one component is the bottleneck, and a failure in one component can cascade to bring down the entire system. Microservices address these limitations by decomposing the monolith into independently operable units, trading the simplicity of a single deployment for the flexibility of independent lifecycle management.

However, microservices introduce substantial distributed systems complexity: network communication replaces function calls, data consistency requires distributed transactions or eventual consistency patterns, debugging spans multiple services, and deployment requires orchestration infrastructure (Kubernetes, service mesh). The decision between monolithic and microservice architectures is not a matter of one being superior to the other but of understanding which complexity profile better fits the organization's size, team structure, deployment requirements, and operational maturity.

Historical Context

The term "microservices" entered common usage around 2012, though the underlying ideas drew from decades of distributed systems research and the Service-Oriented Architecture (SOA) movement of the early 2000s. James Lewis and Martin Fowler's 2014 article "Microservices: a definition of this new architectural term" provided the canonical description that the industry adopted.

Several organizational experiences catalyzed the movement. Amazon's 2002 mandate from Jeff Bezos that all teams must expose their functionality through service interfaces fundamentally restructured the company's engineering culture. Netflix's migration from a monolithic Java application to a microservices architecture (2009-2012) produced the open-source tools (Zuul, Eureka, Hystrix, Ribbon) that formed the first practical microservices toolkit. Spotify's "Squad Model" (2012) demonstrated how microservices enable organizational scaling through autonomous teams aligned to business domains.

The pattern gained mainstream adoption alongside containerization (Docker, 2013) and container orchestration (Kubernetes, 2014), which provided the deployment infrastructure that made running hundreds of services operationally feasible. The service mesh concept (Linkerd 2016, Istio 2017) added a dedicated infrastructure layer for service-to-service communication, further reducing the boilerplate each service needed to implement.

By 2020, the industry had accumulated enough experience to recognize microservices' limitations as clearly as their benefits. The "microservices premium" -- the operational tax of running distributed infrastructure -- became well-documented, and thoughtful practitioners began advocating for the "modular monolith" as a middle ground that provides modularity without distribution. This perspective aligns precisely with the Prismatic Platform's architectural approach.

The Monolith-Microservice Spectrum

Modern architectures exist on a spectrum rather than as a binary choice:

ArchitectureDescriptionDeploymentCommunicationTeam Model
MonolithSingle deployable unit, all code in one processOne artifactFunction callsOne team or shared codebase
Modular monolithSingle deployment with strong internal module boundariesOne artifactFunction callsMultiple teams, shared deployment
MicroservicesMultiple independent deploymentsMany artifactsNetwork (HTTP, gRPC, messaging)Independent teams per service
ServerlessFunctions deployed individually, no server managementPer-functionEvent-drivenPer-function ownership

The Prismatic Platform occupies the "modular monolith" position, using Elixir's umbrella application pattern to achieve microservice-like modularity within a single BEAM deployment. Each umbrella app is an independent bounded context with its own compilation, testing, and supervision tree, but they communicate through in-process function calls rather than network APIs.

Key Principles

PrincipleDescriptionTrade-off
Single responsibilityEach service implements one business capabilityMore services to manage
Data ownershipEach service owns its database/storageCross-service queries require APIs
Independent deploymentServices deploy without coordinating with othersVersion compatibility complexity
Technology freedomEach service can use different languages/frameworksOperational diversity overhead
ResilienceService failure is isolated, does not cascadeRequires circuit breakers, retries
ScalabilityScale individual services based on their loadContainer orchestration overhead
Decentralized governanceTeams choose their own tools and patternsConsistency requires conventions
Design for failureEvery service call can failError handling is pervasive

Communication Patterns

PatternProtocolUse CaseCouplingLatency
Synchronous RESTHTTP/JSONSimple CRUD, queriesHigherMedium
Synchronous gRPCHTTP/2 + ProtobufPerformance-critical, typed APIsHigherLow
Async messagingAMQP, KafkaEvent-driven, eventual consistencyLowerHigher
Event sourcingEvent logAudit trails, temporal queriesLowestVaries
Saga patternChoreography/orchestrationDistributed transactionsMediumHigh

Service Decomposition Strategies

StrategyApproachExampleRisk
By business capabilityEach service maps to a business functionPayment service, Inventory serviceAnemic services
By domain (DDD)Services align with bounded contextsOrder context, Shipping contextOver-decomposition
By data ownershipServices grouped by data domainCustomer data service, Product catalogData duplication
Strangler figIncrementally extract from monolithExtract authentication first, then billingPartial migration
Team topologyServices aligned to team boundariesConway's Law complianceOrganizational rigidity

Challenges of Microservices

ChallengeDescriptionMitigation
Distributed transactionsACID across services is extremely difficultSaga pattern, eventual consistency
Service discoveryServices must find each other dynamicallyService registry (Consul, DNS)
Data consistencyEach service has its own data storeEvent-driven synchronization
DebuggingRequest spans multiple servicesDistributed tracing (OpenTelemetry)
TestingIntegration testing requires running multiple servicesContract testing, consumer-driven contracts
DeploymentCoordinating many deploymentsCI/CD pipelines, Kubernetes
Network latencyEvery service call adds network overheadCaching, batching, co-location
Operational overheadMonitoring, logging, alerting for each serviceCentralized observability platform
Version couplingAPI changes require coordinated rolloutsSemantic versioning, backward compatibility
Data duplicationEach service materializes data it needsEvent-driven materialization, CQRS

The Eight Fallacies of Distributed Computing

Microservices are subject to all eight fallacies of distributed computing, formulated by Peter Deutsch and James Gosling at Sun Microsystems:

  1. The network is reliable
  2. Latency is zero
  3. Bandwidth is infinite
  4. The network is secure
  5. Topology does not change
  6. There is one administrator
  7. Transport cost is zero
  8. The network is homogeneous

Each fallacy represents a class of failures that do not exist in monolithic architectures but must be handled in microservices through retries, circuit breakers, timeouts, encryption, service discovery, and careful capacity planning. The Prismatic Platform avoids these fallacies entirely for internal communication by keeping all services within a single BEAM node, where function calls are local, latency is microseconds, and network partitions are impossible.

Implementation in Prismatic Platform

The Prismatic Platform takes a distinctive approach: achieving microservice-like modularity within a monolithic deployment through its Elixir umbrella architecture. This "modular monolith" pattern provides the organizational benefits of microservices without the distributed systems complexity:

defmodule PrismaticPlatform.Application do
  @moduledoc """
  Top-level application demonstrating umbrella architecture that
  provides microservice-like modularity in a single BEAM deployment.
  Each umbrella app is an independent bounded context with explicit
  dependency declarations and its own supervision tree.

  115 umbrella apps achieve what would require 115 microservices
  in a distributed architecture -- without network overhead,
  distributed transactions, or service discovery.
  """

  use Application

  @impl Application
  @spec start(term(), term()) :: {:ok, pid()} | {:error, term()}
  def start(_type, _args) do
    children = [
      {PrismaticStorageCore.Supervisor, []},
      {PrismaticStorage.Supervisor, []},
      {PrismaticAgents.Supervisor, []},
      {PrismaticPerimeter.Supervisor, []},
      {PrismaticVisitorIntelligence.Supervisor, []},
      {PrismaticSafety.Supervisor, []},
      {PrismaticWeb.Supervisor, []},
      {PrismaticApi.Supervisor, []}
    ]

    Supervisor.start_link(children, strategy: :one_for_one)
  end
end

Umbrella vs Microservices Comparison

AspectPrismatic Umbrella (115 apps)Traditional Microservices
DeploymentSingle BEAM releaseMultiple containers/services
CommunicationFunction calls (in-process)HTTP/gRPC/messaging (network)
Data sharingShared PostgreSQL with schema boundariesDatabase per service
LatencyMicroseconds (function call)Milliseconds (network)
TransactionsACID across apps (same DB)Saga/eventual consistency
Failure isolationOTP supervision treesService mesh, circuit breakers
Independent testingEach app compilable/testable aloneEach service runnable alone
Dependency managementmix.exs declares inter-app depsAPI contracts, versioning
Team boundariesApp ownership (same repo)Service ownership (separate repos)
Operational costSingle deployment to manageN deployments to manage
Hot code reloadNative BEAM capabilityRolling restart required
Shared librariesDirect dependency in mix.exsPublished package or git submodule

Why Umbrella Over Microservices

defmodule Prismatic.ArchitecturalPhilosophy do
  @moduledoc """
  The meta-rule that guides Prismatic's architecture:
  "If the same solution could be written identically in Node.js, it is WRONG."

  Microservices + REST is the Node.js way.
  OTP umbrella + supervision is the Elixir/OTP way.
  The BEAM provides the process isolation, fault tolerance, and
  distribution primitives that microservices frameworks simulate.
  """

  @spec why_not_microservices() :: {:ok, [String.t()]}
  def why_not_microservices do
    reasons = [
      "BEAM processes provide isolation without network overhead",
      "Supervision trees provide fault recovery without service mesh",
      "Message passing provides async communication without message brokers",
      "Distribution provides cross-node deployment without Kubernetes",
      "Hot code reload provides zero-downtime updates without rolling restarts",
      "ETS provides shared state without Redis or distributed caches",
      "Umbrella apps provide bounded contexts without separate repositories"
    ]

    {:ok, reasons}
  end
end

The BEAM virtual machine provides most of what microservices architectures simulate through external infrastructure. Process isolation provides failure boundaries, supervision trees provide automatic recovery, message passing provides asynchronous communication, and distribution provides cross-node deployment. Layering HTTP-based microservices on top of the BEAM means paying the distributed systems tax without receiving its benefits.

When Microservices Are Appropriate

Despite the Prismatic Platform's umbrella approach, microservices remain the right choice in specific organizational and technical contexts:

ContextWhy Microservices Fit
Large organization (100+ engineers)Teams need deployment independence to avoid coordination overhead
Polyglot requirementsDifferent services need different languages (ML in Python, API in Go)
Independent scalingOne component receives 100x the traffic of others
Regulatory isolationPCI/HIPAA components must be physically separated
Acquisition integrationAcquired systems need loose coupling during integration
Team autonomyConway's Law alignment requires service boundaries matching team boundaries

The key insight is that microservices solve organizational scaling problems, not technical ones. If your team is small enough to coordinate deployments and your technology stack is uniform, microservices add complexity without corresponding benefit.

Comparison with Alternatives

ArchitectureBest ForTeam SizeOperational MaturityPrismatic Relevance
MonolithSmall teams, early stage1-10LowStarting point
Modular monolith (umbrella)Medium teams, BEAM/OTP5-50MediumCurrent architecture
MicroservicesLarge organizations, polyglot50+HighNot needed (BEAM handles it)
ServerlessEvent-driven, variable loadAnyLow (managed)Complementary for specific tasks
Service meshMicroservices with advanced networking50+Very highUnnecessary with OTP

Best Practices

  1. Start Monolithic, Extract When Needed: Do not begin with microservices. Start with a well-structured monolith (or umbrella), and extract services only when organizational scaling demands it. Premature decomposition creates complexity without benefit.

  2. Define Clear Boundaries First: Whether using microservices or umbrella apps, establish bounded contexts before writing code. Each boundary should encapsulate a cohesive business capability with a well-defined API. The Prismatic Platform's 115 umbrella apps each represent a distinct bounded context.

  3. Prefer In-Process Communication: When services run on the same platform (BEAM), prefer function calls over network communication. Network calls add latency, failure modes, and serialization overhead that in-process calls avoid.

  4. Use OTP for What Microservices Promise: Process isolation, supervision trees, hot code reload, and distributed Erlang provide the benefits that microservices architectures simulate. Use the platform's native capabilities rather than reimplementing them through infrastructure.

  5. Accept the Trade-offs Consciously: If you choose microservices, accept the full complexity cost: distributed tracing, service discovery, circuit breakers, eventual consistency, contract testing, and deployment orchestration. Partial adoption leads to the worst of both worlds.

  6. Monitor the Tax: Track the operational overhead of your architecture. If your team spends more time managing infrastructure than building features, your architecture is too complex for your organization's maturity.

Use Cases in Prismatic

  • Umbrella Application Architecture: The Prismatic Platform's 115 umbrella applications demonstrate how microservice-like modularity is achieved within a monolithic BEAM deployment, with each app maintaining independent compilation, testing, and supervision.

  • API Gateway Pattern: The prismatic_api application functions as an API gateway, exposing internal umbrella app functionality through a unified REST interface -- a pattern borrowed from microservices architecture.

  • Storage Adapter Abstraction: The adapter pattern across 7 storage backends (ETS, Ecto, Meilisearch, KuzuDB, DuckDB, Redis, PostgreSQL) provides the data isolation that microservices achieve through database-per-service, but without network overhead.

  • Agent Specialization: The 530 AIAD agents operate as specialized units with defined responsibilities, communicating through message passing -- achieving the single-responsibility principle of microservices through OTP processes.

See Also

  • Architecture - Platform architecture and umbrella vs microservices analysis
  • Apps - Catalog of 115 umbrella applications
  • Technologies - Technology stack and architectural patterns

Connect & Contribute

Created by Tomas Korcak (korczis) | Open Source under GHL