CRUD stores only current state, losing history. Event‑Sourcing records every change as an immutable event, enabling you to answer "How did we get here?"

Growing Marijuana with Event‑Sourcing — Introduction

The story of myPlant

Three months ago I planted a marijuana seed in a small pot. I named it myPlant and put it near the window. The first week was exciting — I watered it daily, watched the first leaves sprout, and took photos to track its growth.

Then life got busy. Work deadlines piled up, I traveled for a week, and I forgot a few times — you know how it goes. I still watered myPlant when I remembered, but the intervals became irregular: two weeks would pass, then I’d water it twice in one week, then another long gap.

Today I checked on `myPlant`. The leaves are yellowing, the stem is thin, barely 15 cm tall. Something went wrong, but I can't remember what. Did I water it enough? I honestly can't recall.
Meanwhile, Grandma's plant — same day, same strain — stands at 45 cm with lush green leaves. While I'm trying to remember *if* I watered mine, hers looks ready for a photoshoot.

CRUD: the traditional approach

| id            | height | health | lastWatered         | trimCount |
|---------------|--------|--------|---------------------|-----------|
| myPlant       | 15     | 30     | 2023-10-15T14:30:00 | 0         |
| id            | height | health | lastWatered         | trimCount |
|---------------|--------|--------|---------------------|-----------|
| grandmasPlant | 45     | 95     | 2023-10-30T08:45:00 | 12        |

Both plants are stored as simple rows in a CRUD database, showing only their current state.

The question CRUD cannot answer

Why is myPlant unhealthy while Grandma's plant thrives?

I can see myPlant is in poor condition, but I cannot answer:

With CRUD, I only have a snapshot of myPlant’s current state. The history is gone—overwritten with each update. I can’t debug the problem because I don’t know how myPlant got to this state.

Grandma's secret

"How do you do it, Grandma?" I asked.

She smiled and pulled out a small notebook. "I keep track of everything," she said. "Every time I water it, trim it, or do anything — I write it down with the date"

Her notebook looked like this:

2023-08-01T10:00:00: Seeded
2023-08-03T08:30:00: Watered
2023-08-06T09:15:00: Watered
2023-08-09T07:45:00: Watered
2023-08-12T08:00:00: Watered
2023-08-15T09:30:00: Watered, Trimmed
2023-08-18T08:15:00: Watered
...
2023-10-28T07:30:00: Watered
2023-10-30T08:45:00: Watered

But why does this make a difference?

Looking at her notebook I could immediately see the pattern: consistent watering every 2–3 days. My CRUD snapshot couldn’t tell me that. It only showed the last time I watered myPlant, not the pattern of care.

Grandma’s notebook is Event‑Sourcing — tracking every event that happened, not just the current state.

Event‑Sourcing: storing the full history

With Event‑Sourcing, instead of storing only the current state, we store every event that happened:

type PlantEvent =
  | {
      type: "Seeded";
      plantId: string;
      occured_at: Date;
    }
  | { type: "Watered"; plantId: string; occured_at: Date }
  | { type: "Trimmed"; plantId: string; occured_at: Date }
  | { type: "Died"; plantId: string; occured_at: Date };
myPlant.ts (sporadic care)
const myPlantEvents: PlantEvent[] = [
  { type: "Seeded", plantId: "myPlant", occured_at: new Date("2023-08-01T10:00:00") },
  { type: "Watered", plantId: "myPlant", occured_at: new Date("2023-08-03T08:30:00") },
  { type: "Watered", plantId: "myPlant", occured_at: new Date("2023-08-20T09:15:00") },
  { type: "Died", plantId: "myPlant", occured_at: new Date("2023-10-15T14:30:00") },
];
grandmasPlant.ts (consistent care)
const grandmasPlantEvents: PlantEvent[] = [
  { type: "Seeded", plantId: "grandmasPlant", occured_at: new Date("2023-08-01T10:00:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-02T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-03T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-04T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-05T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-06T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-07T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-08T08:30:00") },
  { type: "Trimmed", plantId: "grandmasPlant", occured_at: new Date("2023-08-09T09:00:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-09T09:15:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-10T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-11T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-12T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-13T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-14T08:30:00") },
  { type: "Trimmed", plantId: "grandmasPlant", occured_at: new Date("2023-08-15T09:00:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-15T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-16T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-17T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-18T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-19T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-20T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-21T08:30:00") },
  { type: "Trimmed", plantId: "grandmasPlant", occured_at: new Date("2023-08-22T09:00:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-22T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-23T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-24T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-25T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-26T08:30:00") },
  { type: "Trimmed", plantId: "grandmasPlant", occured_at: new Date("2023-08-27T09:00:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-27T08:30:00") },
  { type: "Watered", plantId: "grandmasPlant", occured_at: new Date("2023-08-28T08:30:00") },
];

The insight: now we can answer the question. myPlant was watered only twice in the first 19 days before it died from neglect; Grandma’s plant was watered regularly and trimmed twice per week. The events tell the complete story of how we got to the current state — my neglect led to death, while consistent care produced healthy growth.

Summary

Event‑Sourcing provides a fundamentally different approach to managing state:

Storing events (Seeded, Watered, Trimmed, Died) lets us:

  1. Answer questions that CRUD cannot (for example, "Why did myPlant die?")
  2. Understand the complete history of our plants
  3. Derive insights that would be impossible with just a snapshot
  4. Consistent care matters: daily watering and regular trimming lead to healthy growth; neglect leads to death

Further reading:

Comments

Have questions or suggestions? I'd love to hear from you and help improve this content.