# martinwa/growbook architecture

> Architecture diagram of the GitHub repository martinwa/growbook, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-11-13). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/martinwa/growbook
- Repository: https://github.com/martinwa/growbook

## Overview

Growbook is a private, self-hosted Django garden diary and planting planner. The main workflow is an authenticated garden member recording observations or maintaining growing-area, crop, variety, and planting records; the application validates and persists changes, then presents diary searches, registers, cycle cards, and growing-year histories. Photos are processed and stored privately. The README documents backup, restore, appearance, and release indicators as additional capabilities. Some view and operations implementations were not sampled, so those links rely on README statements rather than inspected call paths.

Main entry points: [`core/photo_storage.py`](https://github.com/martinwa/growbook/blob/main/core/photo_storage.py) (Photo bundles), [`growbook/urls.py`](https://github.com/martinwa/growbook/blob/main/growbook/urls.py) (Django routes), [`diary/views.py`](https://github.com/martinwa/growbook/blob/main/diary/views.py) (Diary pages), [`diary/services.py`](https://github.com/martinwa/growbook/blob/main/diary/services.py) (Diary writes)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_runtime["Application runtime"]
  node_router["Django routes<br/>[urls.py]"]
  node_appearance["Theme and release context"]
  node_operations["Operational commands<br/>[backup_garden.py]"]
end

subgraph group_diary["Diary"]
  node_diary_views["Diary pages<br/>[views.py]"]
  node_diary_queries["Diary search and filters<br/>[queries.py]"]
  node_diary_service["Diary writes<br/>[services.py]"]
  node_diary_models[("Diary records<br/>[models.py]")]
end

subgraph group_garden["Garden records"]
  node_register_views["Register pages<br/>[registers.py]"]
  node_register_forms["Register forms<br/>[forms.py]"]
  node_register_service["Register writes<br/>[registers.py]"]
  node_garden_queries["Area and history queries<br/>[queries.py]"]
  node_garden_models[("Garden records<br/>[models.py]")]
end

subgraph group_plantings["Planting lifecycle"]
  node_planting_views["Planting pages<br/>[plantings.py]"]
  node_planting_service["Planting writes<br/>[plantings.py]"]
  node_cycle_service["Cycle operations<br/>[cycles.py]"]
  node_event_views["Planting events<br/>[events.py]"]
end

subgraph group_shared["Shared services"]
  node_photo_storage["Photo bundles<br/>[photo_storage.py]"]
  node_media["Private media operations<br/>[media.py]"]
  node_photo_model[("Photo records<br/>[models.py]")]
  node_access["Entry permissions<br/>[permissions.py]"]
  node_backup["Backup and restore<br/>[backups.py]"]
end

node_member(("Garden member"))

node_member -->|"opens pages"| node_router
node_router -->|"routes diary"| node_diary_views
node_router -->|"routes registers"| node_register_views
node_router -->|"routes plantings"| node_planting_views
node_diary_views -->|"saves entries"| node_diary_service
node_diary_views -->|"queries diary"| node_diary_queries
node_diary_service -->|"writes entries"| node_diary_models
node_diary_views -->|"checks permissions"| node_access
node_diary_service -->|"stages photos"| node_photo_storage
node_register_views -->|"validates input"| node_register_forms
node_register_views -->|"saves records"| node_register_service
node_register_service -->|"writes records"| node_garden_models
node_register_views -->|"reads records"| node_garden_queries
node_register_service -->|"saves photos"| node_photo_storage
node_planting_views -->|"saves plantings"| node_planting_service
node_planting_views -->|"reads histories"| node_garden_queries
node_planting_service -->|"creates cycles"| node_cycle_service
node_planting_service -->|"writes plantings"| node_garden_models
node_event_views -.->|"records events"| node_garden_models
node_planting_service -->|"saves photos"| node_photo_storage
node_photo_storage -->|"resolves paths"| node_media
node_photo_storage -->|"checks references"| node_photo_model
node_diary_service -->|"creates photos"| node_photo_model
node_appearance -->|"provides theme"| node_member
node_operations -.->|"runs backup"| node_backup

click node_router "https://github.com/martinwa/growbook/blob/main/growbook/urls.py"
click node_diary_views "https://github.com/martinwa/growbook/blob/main/diary/views.py"
click node_diary_queries "https://github.com/martinwa/growbook/blob/main/diary/queries.py"
click node_diary_service "https://github.com/martinwa/growbook/blob/main/diary/services.py"
click node_diary_models "https://github.com/martinwa/growbook/blob/main/diary/models.py"
click node_register_views "https://github.com/martinwa/growbook/blob/main/garden/views/registers.py"
click node_register_forms "https://github.com/martinwa/growbook/blob/main/garden/forms.py"
click node_register_service "https://github.com/martinwa/growbook/blob/main/garden/services/registers.py"
click node_garden_queries "https://github.com/martinwa/growbook/blob/main/garden/queries.py"
click node_garden_models "https://github.com/martinwa/growbook/blob/main/garden/models.py"
click node_planting_views "https://github.com/martinwa/growbook/blob/main/garden/views/plantings.py"
click node_planting_service "https://github.com/martinwa/growbook/blob/main/garden/services/plantings.py"
click node_cycle_service "https://github.com/martinwa/growbook/blob/main/garden/services/cycles.py"
click node_event_views "https://github.com/martinwa/growbook/blob/main/garden/views/events.py"
click node_photo_storage "https://github.com/martinwa/growbook/blob/main/core/photo_storage.py"
click node_media "https://github.com/martinwa/growbook/blob/main/core/media.py"
click node_photo_model "https://github.com/martinwa/growbook/blob/main/core/models.py"
click node_access "https://github.com/martinwa/growbook/blob/main/diary/permissions.py"
click node_appearance "https://github.com/martinwa/growbook/blob/main/core/context_processors.py"
click node_backup "https://github.com/martinwa/growbook/blob/main/core/backups.py"
click node_operations "https://github.com/martinwa/growbook/blob/main/core/management/commands/backup_garden.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_router,node_appearance,node_operations toneBlue
class node_diary_views,node_diary_queries,node_diary_service,node_diary_models toneAmber
class node_register_views,node_register_forms,node_register_service,node_garden_queries,node_garden_models toneMint
class node_planting_views,node_planting_service,node_cycle_service,node_event_views toneRose
class node_photo_storage,node_media,node_photo_model,node_access,node_backup,node_member toneIndigo
```

## Components

### Application runtime

- [Django routes](https://github.com/martinwa/growbook/blob/main/growbook/urls.py): `growbook/urls.py`
- [Theme and release context](https://github.com/martinwa/growbook/blob/main/core/context_processors.py): `core/context_processors.py`
- [Operational commands](https://github.com/martinwa/growbook/blob/main/core/management/commands/backup_garden.py): `core/management/commands/backup_garden.py`

### Diary

- [Diary pages](https://github.com/martinwa/growbook/blob/main/diary/views.py): `diary/views.py`
- [Diary search and filters](https://github.com/martinwa/growbook/blob/main/diary/queries.py): `diary/queries.py`
- [Diary writes](https://github.com/martinwa/growbook/blob/main/diary/services.py): `diary/services.py`
- [Diary records](https://github.com/martinwa/growbook/blob/main/diary/models.py): `diary/models.py`

### Garden records

- [Register pages](https://github.com/martinwa/growbook/blob/main/garden/views/registers.py): `garden/views/registers.py`
- [Register forms](https://github.com/martinwa/growbook/blob/main/garden/forms.py): `garden/forms.py`
- [Register writes](https://github.com/martinwa/growbook/blob/main/garden/services/registers.py): `garden/services/registers.py`
- [Area and history queries](https://github.com/martinwa/growbook/blob/main/garden/queries.py): `garden/queries.py`
- [Garden records](https://github.com/martinwa/growbook/blob/main/garden/models.py): `garden/models.py`

### Planting lifecycle

- [Planting pages](https://github.com/martinwa/growbook/blob/main/garden/views/plantings.py): `garden/views/plantings.py`
- [Planting writes](https://github.com/martinwa/growbook/blob/main/garden/services/plantings.py): `garden/services/plantings.py`
- [Cycle operations](https://github.com/martinwa/growbook/blob/main/garden/services/cycles.py): `garden/services/cycles.py`
- [Planting events](https://github.com/martinwa/growbook/blob/main/garden/views/events.py): `garden/views/events.py`

### Shared services

- [Photo bundles](https://github.com/martinwa/growbook/blob/main/core/photo_storage.py): `core/photo_storage.py`
- [Private media operations](https://github.com/martinwa/growbook/blob/main/core/media.py): `core/media.py`
- [Photo records](https://github.com/martinwa/growbook/blob/main/core/models.py): `core/models.py`
- [Entry permissions](https://github.com/martinwa/growbook/blob/main/diary/permissions.py): `diary/permissions.py`
- [Backup and restore](https://github.com/martinwa/growbook/blob/main/core/backups.py): `core/backups.py`

### Other components

- **Garden member**

## Connections

- Garden member → Django routes: opens pages
- Django routes → Diary pages: routes diary
- Django routes → Register pages: routes registers
- Django routes → Planting pages: routes plantings
- Diary pages → Diary writes: saves entries (evidence: `diary/services.py`)
- Diary pages → Diary search and filters: queries diary (evidence: `diary/queries.py`)
- Diary writes → Diary records: writes entries (evidence: `diary/services.py`)
- Diary pages → Entry permissions: checks permissions
- Diary writes → Photo bundles: stages photos (evidence: `diary/services.py`)
- Register pages → Register forms: validates input (evidence: `garden/views/registers.py`)
- Register pages → Register writes: saves records (evidence: `garden/views/registers.py`)
- Register writes → Garden records: writes records (evidence: `garden/services/registers.py`)
- Register pages → Area and history queries: reads records (evidence: `garden/views/registers.py`)
- Register writes → Photo bundles: saves photos (evidence: `garden/services/registers.py`)
- Planting pages → Planting writes: saves plantings (evidence: `garden/views/plantings.py`)
- Planting pages → Area and history queries: reads histories (evidence: `garden/views/plantings.py`)
- Planting writes → Cycle operations: creates cycles (evidence: `garden/services/plantings.py`)
- Planting writes → Garden records: writes plantings (evidence: `garden/services/plantings.py`)
- Planting events → Garden records: records events
- Planting writes → Photo bundles: saves photos (evidence: `garden/services/plantings.py`)
- Photo bundles → Private media operations: resolves paths (evidence: `core/photo_storage.py`)
- Photo bundles → Photo records: checks references (evidence: `core/photo_storage.py`)
- Diary writes → Photo records: creates photos (evidence: `diary/services.py`)
- Theme and release context → Garden member: provides theme
- Operational commands → Backup and restore: runs backup
