From Nextcloud to Django: Designing a Farm Sync Architecture with DRF
Modern applications rarely live in a single system. As projects grow, different components begin to specialize: one system handles identity and user workflows, while another focuses on computation and domain logic.
This week I explored a small but interesting distributed architecture problem: how to synchronize farm data between a Nextcloud application and a Django REST API backend.
The goal was simple in theory:
- Nextcloud provides the user interface.
- Django performs geospatial computation (NDVI, raster processing).
- Farm data must stay consistent between the two systems.
But as any engineer knows, “simple in theory” is where architecture decisions start to matter.
The Initial Problem
In the system I’m building, users manage farms from a Nextcloud application while a Django service handles geospatial workloads.
The challenge was deciding where farm data should live and how it should propagate between systems.
Three architectural questions emerged:
- Which system is the source of truth?
- How do we synchronize data between services?
- How do we avoid identity conflicts between systems?
These are classic distributed systems questions, even in relatively small projects.
Option 1: Bidirectional Sync (The Dangerous Path)
One tempting solution is letting both systems create farms and then syncing them.
Nextcloud <--> Django
At first glance this feels flexible. In practice it creates difficult problems:
- conflicting updates
- race conditions
- reconciliation logic
- versioning requirements
Large distributed databases solve this with vector clocks and conflict resolution strategies. For most applications, that complexity is unnecessary.
So I rejected bidirectional replication early.
Option 2: API-First Architecture
Instead of replicating farms between databases, Nextcloud simply delegates creation to the Django API.
When a user creates a farm in Nextcloud:
User
↓
Nextcloud UI
↓
Nextcloud Controller
↓
POST /api/v1/farms/ (Django REST Framework)
↓
Django database
Django becomes the source of truth for farm data, while Nextcloud acts as the interface layer.
This pattern has several advantages.
Immediate Consistency
Since farms are created directly in Django, the backend always has the latest data.
There is no delayed replication.
Clear Ownership
Each system has a defined responsibility:
- Nextcloud – user interface, identity, workflow
- Django – domain logic, geospatial processing, data storage
Clear boundaries reduce architectural complexity.
Extensibility
Once Django exposes a clean API, other systems can integrate easily:
- mobile apps
- data pipelines
- satellite processing services
- analytics dashboards
Everything interacts with the same API.
Solving the Cross-System Identity Problem
Once multiple systems talk about the same object, a subtle problem appears:
identity consistency.
If each system generated its own farm IDs, synchronization would become fragile:
Nextcloud Farm ID = 12
Django Farm ID = 47
Now every integration requires mapping tables.
Instead, the architecture introduces a stable external identifier.
Nextcloud generates a UUID called:
external_farm_id
That UUID is sent to Django whenever a farm is synchronized.
Conceptually the model looks like this:
Farm
├─ id (internal database id)
└─ external_farm_id (shared UUID)
Now both systems reference the farm using the same identifier.
When Nextcloud syncs a farm:
POST /api/v1/farms/sync
Payload example:
{
"external_farm_id": "uuid",
"external_user_id": "nextcloud_uid",
"name": "Demo Farm",
"bbox": "...",
"centroid": "..."
}
This approach provides several benefits.
No ID Collisions
UUIDs are globally unique, preventing conflicts between systems.
Clean Synchronization
Updates and raster requests can reference farms using the same external identifier.
/api/v1/farms/{external_farm_id}
Future Integration
If other services appear later (mobile apps, analytics pipelines, satellite processors), they can all reference farms using the same UUID.
This pattern is common in distributed systems and prevents a large class of synchronization bugs.
Identity Translation Between Systems
Another challenge is mapping users between Nextcloud and Django.
Nextcloud users authenticate normally and then communicate with Django using an integration token.
Conceptually:
Nextcloud User
↓
Integration Token
↓
Django API
↓
Farm Sync
The Django service stores farms under a service user while still preserving the original external_user_id from Nextcloud.
This keeps authentication simple while preserving user ownership information.
Measuring the Integration Layer
During development I analyzed the Nextcloud app using cloc to understand the size of the integration layer.
The results showed roughly 11,000 lines of code, split mainly between PHP backend logic and JavaScript UI.
Around this size, architecture decisions begin to matter more than raw implementation.
Systems become complex enough that clear service boundaries become essential.
Lessons Learned
Several practical lessons emerged from this integration work.
1. Avoid bidirectional replication
Two systems writing to the same domain model creates unnecessary complexity.
2. Establish a clear source of truth
In this architecture, Django owns farm data while Nextcloud orchestrates the workflow.
3. Use stable external identifiers
UUIDs dramatically simplify synchronization across systems.
4. Prefer API-first architectures
APIs make it easier to expand systems and integrate future services.
5. Keep compute close to the data
Since Django handles geospatial processing, storing farms there keeps the compute layer efficient.
Final Thoughts
What started as a simple integration between Nextcloud and Django turned into a useful exercise in distributed system design.
Even relatively small systems benefit from clear service boundaries and stable identity strategies.
By combining:
- an API-first architecture
- external UUID identifiers
- and clear ownership of farm data
the system stays simple today while remaining extensible for future services like satellite analytics or mobile farming applications.
Sometimes good architecture isn’t about complexity at all — it’s about clarity of responsibility between systems.
Top comments (0)