Why take on a backend in the first place?
In my posts on local-first architecture and multi-device synchronization, I covered how Accumoo saves changes locally and synchronizes them across devices. This time, I want to talk about the shared Mohojo Werks backend behind this synchronization Sync (synchronization) sends changes from your device to Mohojo Werks’ servers and brings those changes to your other devices when they sync. This keeps your Accumoo data up to date across devices. —and why I decided to build it myself.
The architecture supports multiple applications, with shared user accounts and application-specific permissions, data, and business rules. Accumoo uses this foundation today, and I intend to use it for future Mohojo Werks applications.
Accumoo’s iPhone, iPad, and Apple Watch apps communicate through a Rails API backed by PostgreSQL. For Accumoo, one of this backend’s responsibilities is enforcing the business rules that govern how changes from different devices are reconciled and ultimately made available to other devices.
Open full-size diagram: shared identity and application-specific access
The bottom line is: I needed control over these decisions, along with the freedom to change the backend as Accumoo evolves. I also wanted the option to run my applications on the hosting provider of my choosing—or possibly, one day, on my own servers.
This was not an easy decision. Building the backend myself, also means I’m responsible for maintaining it, securing it, and making sure I can recover when something goes wrong. All these responsibilities come with making a decision like this.
What I needed to control
Accumoo’s backend needs to enforce consistent business rules across supported API versions. For example, an older iPhone app might update an Event An activity you want to track, such as reading, walking, or staying hydrated. using API version 1, while a newer app might use API version 2. As I introduce new Event properties, older clients must still be able to update the properties they understand, without inadvertently wiping out newer properties they don’t. I needed control over how each API version validates and applies these changes; and, in this way, allow Accumoo to evolve without breaking older supported clients.
I control when I deploy the backend, but I don’t control when users update their apps.
Open full-size diagram: supported API versions and a shared Event model
The same applies to authentication and authorization. In more general terms, the Mohojo Werks backend supports a shared user account across multiple applications, including Accumoo. Users can sign in with the same account, while authorization and access to data remain application-specific. Owning this part of the backend allows me to centralize authentication, define which applications an account can access, and enforce each user’s application-specific permissions consistently across clients.
Why Rails works for me
Plenty of frameworks can support this backend. My main reasons for choosing Rails are simplicity and speed of development.
Rails’ convention over configuration approach makes a lot of decisions for me: where code belongs, how it’s named, and how the pieces fit together. These conventions simplify the work and let me get on with building features. They’re also useful when I come back to code months later and need to change things.
Testing is another reason. With RSpec, I can test individual operations and exercise the API through requests, including the failure cases. The Rails ecosystem is tried and tested, with established testing tools, libraries, and documentation I can rely on as I develop and maintain the backend.
Rails offers a practical foundation for a backend I need to maintain over time. This backend is the backbone of Mohojo Werks, supporting Accumoo today and the applications we build going forward. I can keep building on this foundation and keep the implementation organized as it grows.
Keeping shared and application-specific code separate
I partition the backend code into application-agnostic and application-specific responsibilities:
- Application-agnostic code handles shared concerns, such as authentication and linking user accounts to applications. I can reuse this code across Mohojo Werks applications.
-
Application-specific code handles a particular application’s behavior.
Accumoo’s code uses the
Accumoo::namespace and includes its Events, history A read-only view of recorded counts and past periods. Open an activity's menu to find it. , Challenges A time-limited goal with a clear finish line. Choose a difficulty level and record progress toward completing it. , and associated business rules.
Application-agnostic code can be reused across Mohojo Werks applications. Application-specific rules have a clear home as well, so shared code doesn’t become a collection of special cases for individual apps. Less duplication and clear responsibilities make the backend easier to test, change, and maintain as Mohojo Werks adds applications.
Open full-size diagram: shared code and application-specific namespaces
Command-driven architecture
I organize application operations into command objects, keeping their business logic separate from HTTP request handling. Controllers dispatch these operations to the appropriate commands, which I can test independently.
I’ll cover more about how I use this command-driven paradigm to organize application operations in the next article.