Devlog

The data model: how Vernal thinks about a garden

The first question worth answering when you’re building a tool like this is: what is a garden, from a data perspective? The obvious answer, a garden is a collection of plants, leads you straight into the same limitations every existing tool has. If that’s your model, you end up with a list manager with some planning features bolted on. It won’t do analytics. It won’t track relationships. It won’t tell you anything interesting across seasons.

Vernal starts from a different assumption. A garden is a network of entities with relationships that change over time. Getting that model right isn’t a backend implementation detail, it determines what the app can ever know about your garden.

The core entities

There are six entity types at the heart of the data model. Everything else is either a relationship between them or a time-series record hanging off one of them.

Garden
  └── Beds []
        └── Zones []
              └── Plantings [] ← a seed in a location at a time
  └── Catalogue []             ← seeds you own, regardless of whether planted
  └── Amendments []            ← soil work, fertilizer, pH adjustments
  └── Harvests []              ← yield records tied to a planting
  └── WeatherRecords []        ← temperature, rainfall, frost dates

The key design choice is that Planting is its own entity. Not a property on a Seed. A planting represents a specific seed variety, in a specific zone, during a specific season. That separation is what makes cross-season comparison possible.

The relationship problem

The entities above are straightforward. The harder part is the relationships between them, which fall into a few categories:

Spatial relationships

Zones are physical spaces within a bed. They have a position, a size, and a history of what’s been planted in them. Tracking zone-level history is what lets Vernal know that you’ve grown nightshades in a particular spot for three consecutive seasons — which matters for crop rotation logic and for understanding soil depletion patterns.

Companion relationships

Companion planting isn’t just a lookup table. It’s a graph. Each seed variety has relationships with other varieties; some beneficial, some neutral, some inhibitory. The data model represents these as weighted, directed edges.

  • Beneficial relationships are bidirectional — both plants gain something
  • Inhibitory relationships are directional — fennel suppresses others, but nothing suppresses fennel
  • The proximity radius matters: not all relationships activate at the same distance
  • Strength is weighted, so the canvas can show caution versus full conflict

Temporal relationships

Almost every record in the system is time-scoped. A planting has a start date and (eventually) an end date. Harvests are attached to a planting ID and a date. Amendments are dated. Weather records are daily. This is intentional. It’s what makes the analytics layer possible later.

The goal is that any question you could answer by flipping through five years of garden notebooks should be answerable with a query. That’s the bar. If the data model can’t support the query, it’s the data model’s fault.

Why this matters for analytics

The analytics features Vernal is being built toward: cross-season yield comparisons, germination rate tracking, soil amendment correlation, aren’t possible with a flat data model. They require the ability to join plantings to harvests, filter by variety, group by location, and span multiple years.

[ Diagram: entity relationship overview ]

Entity relationships in Vernal’s data model. Plantings sit at the center — connected to seeds, zones, harvests, and amendments.

The next post will cover how this model maps onto the actual database schema and what that means for query performance at the scale Vernal is planning for.


Questions or pushback on the data model? The repository is at github.com/vernal-garden/vernal. Open an issue or reach out directly.