Elixir Architecture: Multitier vs Singleton in Cloud and Embedded Systems
Cloud Elixir apps avoid distributed state to scale horizontally, but embedded devices flip this completely—they thrive on internally distributed state because they need resilience from hardware failures, not the ability to spin up more nodes.
Read Original Summary used for search
TLDR
• Multitier architecture works great for Elixir cloud apps because it defers hard distributed systems problems to a single-writer database—you avoid eventual consistency, split brains, and quorum issues
• Singleton GenServers holding state are anti-patterns in cloud systems the moment you cluster—that's distributed state inviting headaches you don't need
• Embedded devices are fundamentally different: everything is "n of one" with physical hardware you can't just tear down and restart from a cloud UI
• Embedded systems use GenServers specifically to ensure singleton access to hardware and PubSub to distribute sensor readings to UI, cloud uplink, and smart home integrations
• Isolation via GenServers provides resilience—temperature sensor crashing stops temperature updates but doesn't take down the whole UI or device connectivity
In Detail
The standard advice for Elixir cloud architecture is multitier: presentation layer (API/LiveView), application/logic layer, and data layer (usually Postgres). This works brilliantly because it defers the hard distributed systems problems—you avoid eventual consistency, split brains, and quorum issues by keeping a centralized single-writer database. Phoenix and Ecto give you nice OTP patterns local to each node, and clustering with Phoenix PubSub is straightforward because the hard state problems live in Postgres. This is why singleton GenServers holding state become anti-patterns—the moment you add nodes, that's distributed state causing headaches. Scaling the database is the genuinely hard part, which is why most systems stick to this architecture.
Embedded devices flip this completely. Everything is "n of one"—singular resources, physical hardware attached to walls that can't be torn down and restarted. Devices use GenServers specifically to ensure singleton access to hardware (you don't want fights on the wire about backlight brightness). PubSub patterns work brilliantly: one temperature sensor reading gets distributed to the UI compositor, cloud uplink, and Thread radio for smart home integration. The device becomes a heterogeneous distributed system where different components do different work, continuously in flux and eventually consistent by default. You filter bad sensor values, set sane bounds, and limit how often you change things to keep the device moving through states in a stable manner.
The key insight: isolation via GenServers provides resilience in embedded systems. A temperature sensor crashing means the UI stops updating temperature but doesn't take down overall responsiveness or connectivity. Cloud systems avoid distributed state to scale; embedded devices don't need to "scale" and thrive on internally distributed state for resilience against hardware failures.