Skip to content

Architecture patterns

Design principles and infrastructure patterns that guide how Jersal projects are built and deployed.

Core principles

1. Shared infrastructure, independent deployments

Shared resources (databases, state backends) live in jersal-projects-core. Each project deploys independently through its own CI/CD pipeline.

graph TB
    CORE["jersal-projects-core<br/>(shared infra)"] --> |PostgreSQL, RBAC| P1["Project A"]
    CORE --> |PostgreSQL, RBAC| P2["Project B"]
    P1 --> |independent deploy| D1["Azure SWA / App Service"]
    P2 --> |independent deploy| D2["Azure SWA / App Service"]

Why: Shared infrastructure avoids duplication and cost. Independent deployments prevent one project's release from blocking another.

2. Infrastructure as code

All infrastructure is defined in Terraform. No manual Azure portal changes.

Why: Reproducibility, version history, peer review via PRs, and automated provisioning.

3. Secrets never in code

  • OIDC for CI/CD authentication (no stored Azure credentials)
  • Sensitive variables in GitHub Secrets, never committed
  • .tfvars files gitignored
  • Future: Azure Key Vault for application secrets

Why: Security. Credentials in code are the most common source of breaches.

4. CI/CD as the deployment mechanism

All deployments happen through GitHub Actions on the release branch. Local applies are possible but discouraged.

Why: Consistency, auditability, and reduced risk of drift between environments.

Infrastructure patterns

Region strategy

Resource type Region Reason
Compute, databases swedencentral Primary region, EU data residency
Static Web Apps westeurope SWA doesn't support swedencentral

State isolation

Each infrastructure concern has its own Terraform state backend:

State Storage account Scope
shared.tfstate stjersalprojcore PostgreSQL, shared RBAC
site.tfstate stjersalprojcoresite Portfolio SWA, site RBAC

Why: Isolation prevents one terraform apply from accidentally affecting unrelated resources. Blast radius is contained.

Module reuse

Common resource patterns are extracted into modules under terraform/modules/:

Module What it creates
resource-group Azure Resource Group with tags
postgres-flex PostgreSQL Flexible Server + databases + firewall rules
rbac Role assignments from a list of objects
swa Azure Static Web App

When to create a module: When a resource pattern appears (or will appear) in more than one environment.

Decision framework

When making architecture decisions:

  1. Document the decision as an ADR
  2. Prefer boring technology -- well-understood tools over cutting-edge
  3. Optimize for maintainability -- code is read far more than it's written
  4. Start simple -- add complexity only when the current approach fails
  5. Measure before optimizing -- don't solve problems that don't exist yet