πŸ—οΈ MODULAR ARCHITECTURE

Constructive Workspaces: A New Way to Organize PostgreSQL Projects

Database development shouldn't feel like you're stuck in 2005. When you want to build a new feature, you shouldn't spend hours configuring migration tools, manually tracking dependencies, or wondering if your schema changes will deploy in the right order.

The Problem: Monolithic Database Development

Traditional database development treats your entire schema as one giant migration chain. You write SQL files, number them sequentially, and hope they run in order. When you want to reuse a schema across projects, you copy-paste SQL files and lose all version history. When modules depend on each other, you manually track what needs to deploy first.

This doesn't scale.

Frontend developers expect modularityβ€”they install packages with npm install, compose applications from reusable components, and let the package manager handle dependency resolution. Database development deserves the same sophistication.

Constructive Workspaces: npm for PostgreSQL

constructive workspaces bring npm-style modularity to PostgreSQL. Instead of monolithic migration scripts, you build focused modules that declare their dependencies explicitly. Instead of manual deployment orchestration, you get automatic dependency resolution.

A constructive workspace is a pnpm monorepo containing multiple database modules. Each module is self-contained with its own migrations, dependencies, and version tags. When you deploy, constructive discovers all modules, resolves the dependency graph, and deploys everything in the correct order.

πŸ“

Workspace

The top-level directory containing your entire project. It has a constructive.json configuration file and a packages/ directory where modules live.

Think of it like: Your npm project root with package.json

πŸ“¦

Module

A self-contained database package inside the workspace. Each module has its own constructive.plan file, .control file, and deploy/, revert/, verify/ directories.

Think of it like: An individual npm package

Example Workspace Structure

my-workspace/              # Workspace root
β”œβ”€β”€ constructive.json      # Workspace config
β”œβ”€β”€ packages/              # Modules directory
β”‚   β”œβ”€β”€ users/            # Module: user management
β”‚   β”‚   β”œβ”€β”€ constructive.plan
β”‚   β”‚   β”œβ”€β”€ users.control
β”‚   β”‚   β”œβ”€β”€ deploy/
β”‚   β”‚   β”œβ”€β”€ revert/
β”‚   β”‚   └── verify/
β”‚   └── organizations/    # Module: org management
β”‚       β”œβ”€β”€ constructive.plan
β”‚       β”œβ”€β”€ organizations.control
β”‚       β”œβ”€β”€ deploy/
β”‚       β”œβ”€β”€ revert/
β”‚       └── verify/

How Modules Declare Dependencies

Modules declare dependencies in their .control file, similar to package.json:

# organizations.control
comment = 'Organization management module'
default_version = '0.0.1'
requires = 'users,uuid-ossp'

This tells constructive that the organizations module depends on the users module and the uuid-ossp PostgreSQL extension. When you deploy organizations, constructive automatically deploys users first.

Automatic Dependency Resolution

When you run constructive deploy, constructive:

  • 1Scans your workspace for all modules (by finding .control files)
  • 2Builds a dependency graph from the requires fields
  • 3Resolves the topological order (which modules must deploy first)
  • 4Deploys modules in the correct sequence
  • 5Tracks what's been deployed in the constructive_migrate schema

You never manually specify deployment order. constructive handles the orchestration automatically.

Cross-Module References

Modules can reference changes from other modules in their dependency declarations. If your organizations module needs a specific change from the users module, you can declare it:

-- Deploy tables/memberships
-- requires: users:tables/users

CREATE TABLE organizations.memberships (
  user_id UUID REFERENCES users(id),
  organization_id UUID REFERENCES organizations(id)
);

The -- requires: users:tables/users comment tells constructive that this change depends on the tables/users change from the users module. constructive ensures users:tables/users deploys before this change.

🧩

Composability

Build reusable modules that work across projects

πŸ”—

Explicit Dependencies

Dependencies are declared in code, not documentation

πŸ”’

Transactional Safety

All deployments happen in transactions with automatic rollback

⚑

Fast Iteration

TypeScript-based engine deploys complex schemas in milliseconds

πŸ”„

Cross-Project Reuse

Publish modules to npm and install them with constructive install

🎯

Zero Config

No build tools, no manual migration management needed

The Future of Database Development

The future of database development is modular, composable, and fast. constructive workspaces bring the best practices from frontend development to PostgreSQLβ€”giving you the tools to build databases the way you build applications.

Ready to get started?

Stay in the loop

Follow our journey. Watch us build. Be the first to know.

Building in public. Open source from day one.