Skip to content

Latest commit

 

History

History
715 lines (488 loc) · 16.6 KB

File metadata and controls

715 lines (488 loc) · 16.6 KB

Getting Started Guide: Clone, Setup, and Add a New Module

This guide will walk you through the complete process of cloning this repository as a template, setting it up, and adding a new module from scratch.

Table of Contents

  1. Prerequisites
  2. Step 1: Clone the Repository
  3. Step 2: Setup Project
  4. Step 3: Configure the Project
  5. Step 4: Start Infrastructure
  6. Step 5: Add a New Module
  7. Step 6: Register the Module
  8. Step 7: Generate Code
  9. Step 8: Run Migrations
  10. Step 9: Test Your Module
  11. Step 10: Run the Server

Prerequisites

Before you begin, ensure you have the following installed:


Step 1: Clone the Repository

Clone this repository to use it as a template for your project:

# Clone the repository
git clone https://github.com/LoopContext/go-modulith-template.git my-project
cd my-project

# Remove the existing git history (optional, if you want a fresh start)
rm -rf .git
git init
git add .
git commit -m "Initial commit from go-modulith-template"

Note: If you're using this as a GitHub template, you can use the "Use this template" button on GitHub, which will create a new repository with a clean history.


Step 2: Setup Project

Option A: Quick Setup (Recommended)

For the fastest setup, use the automated quickstart script:

just quickstart

This will automatically:

  • Validate your environment
  • Install missing development tools
  • Start Docker infrastructure
  • Run database migrations
  • Optionally run seed data

Option B: Manual Setup

2.1 Install Dependencies

Install all required development tools:

just install-deps

This will install:

  • migrate - Database migration tool
  • sqlc - Type-safe SQL code generator
  • buf - Protocol buffer compiler
  • air - Hot reload tool for development
  • golangci-lint - Go linter
  • gqlgen - GraphQL code generator (optional)
  • mockgen - Mock generator for testing

Verify installations:

# Check that tools are installed
which migrate sqlc buf air golangci-lint

# Or run validation
just validate-setup

Step 3: Configure the Project

3.1 Update Module Name (Optional)

If you want to change the Go module name, update go.mod:

# Edit go.mod and change the module path
# From: module github.com/LoopContext/go-modulith-template
# To:   module github.com/your-org/your-project

Then update all imports in the codebase. You can use a find-and-replace tool or script.

3.2 Configure Database and Environment

The project uses a flexible configuration system with the following priority:

  1. Environment variables (highest priority)
  2. .env file
  3. configs/server.yaml (default configuration)

Option A: Use YAML configuration (recommended for development)

Edit configs/server.yaml:

env: dev
log_level: debug # debug, info, warn, error
http_port: 8000
grpc_port: 9000
service_name: modulith-server
db_dsn: postgres://postgres:postgres@localhost:5432/modulith_demo?sslmode=disable

# Database connection pool settings
db_max_open_conns: 25
db_max_idle_conns: 25
db_conn_max_lifetime: 5m
db_connect_timeout: 10s

# Timeouts
read_timeout: 5s # HTTP server read timeout
write_timeout: 10s # HTTP server write timeout
shutdown_timeout: 30s # Graceful shutdown timeout

auth:
    jwt_secret: your-secret-key-at-least-32-bytes-long-change-this

Option B: Use environment variables

Create a .env file (optional):

# Copy example if available, or create new
cat > .env <<EOF
ENV=dev
LOG_LEVEL=debug
HTTP_PORT=8000
GRPC_PORT=9000
DB_DSN=postgres://postgres:postgres@localhost:5432/modulith_demo?sslmode=disable
JWT_SECRET=your-secret-key-at-least-32-bytes-long-change-this
EOF

Important: Change the JWT_SECRET to a secure random string (at least 32 bytes) before deploying to production.


Step 4: Start Infrastructure

Note: If you used just quickstart, this step is already complete. Skip to Step 5.

Start the required infrastructure services (PostgreSQL, Valkey, etc.):

just docker-up

This starts:

Verify services are running:

docker-compose ps

# Or run comprehensive diagnostics
just doctor

You should see all services in "Up" status.

Tip: To start only the database and Valkey (faster startup), use just docker-up-minimal.


Step 5: Add a New Module

Now let's add a new module. For this example, we'll create an "order" module:

just new-module order

This command will:

  • Create the module directory structure
  • Generate boilerplate code from templates
  • Create migration files
  • Create proto definitions
  • Generate Air configuration for hot reload
  • Update sqlc.yaml with the new module

Generated structure:

modules/order/
├── module.go                    # Module implementation
├── internal/
│   ├── service/
│   │   └── service.go          # Business logic
│   ├── repository/
│   │   └── repository.go     # Data access layer
│   └── db/
│       └── query/
│           └── order.sql      # SQL queries
└── resources/
    └── db/
        ├── migration/          # Database migrations
        └── seed/               # Seed data

proto/order/v1/
└── order.proto                 # gRPC service definition

cmd/order/
└── main.go                     # Standalone service entry point

configs/
└── order.yaml                  # Module configuration

.air.order.toml                  # Hot reload config for this module

What was generated:

  1. Module structure - Complete module with service, repository, and database layers
  2. gRPC service - Protocol buffer definition for the module
  3. Database migrations - Initial schema migration files
  4. SQL queries - Template SQL queries for sqlc
  5. Configuration - Module-specific YAML configuration
  6. Standalone binary - Entry point to run the module independently

Step 6: Register the Module

After scaffolding, you need to register the module in the main server. Edit cmd/server/setup/registry.go:

Find the RegisterModules function:

// RegisterModules registers all modules with the registry.
func RegisterModules(reg *registry.Registry) {
    // Register all modules here
    reg.Register(auth.NewModule())
    // Add more modules as needed:
    // reg.Register(order.NewModule())
    // reg.Register(payment.NewModule())
}

Add your new module:

// RegisterModules registers all modules with the registry.
func RegisterModules(reg *registry.Registry) {
    // Register all modules here
    reg.Register(auth.NewModule())
    reg.Register(order.NewModule())  // Add this line
    // Add more modules as needed:
    // reg.Register(payment.NewModule())
}

Add the import at the top of the file:

import (
    // ... existing imports ...
    "github.com/LoopContext/go-modulith-template/modules/auth"
    "github.com/LoopContext/go-modulith-template/modules/order"  // Add this line
    // ... rest of imports ...
)

Step 7: Generate Code

Now generate the code from your proto definitions and SQL queries:

7.1 Generate gRPC Code

Generate Go code from your Protocol Buffer definitions:

just proto

This will:

  • Generate gRPC service code from proto/order/v1/order.proto
  • Create client and server stubs
  • Generate OpenAPI/Swagger documentation in gen/openapiv2/

7.2 Generate SQL Code

Generate type-safe Go code from your SQL queries:

just sqlc

This will:

  • Generate Go code from SQL queries in modules/order/internal/db/query/
  • Create type-safe database access code in modules/order/internal/db/store/
  • Generate repository interfaces

Verify generated code:

# Check that files were generated
ls -la gen/go/proto/order/v1/
ls -la modules/order/internal/db/store/

Step 8: Run Migrations

Run database migrations to create the schema for your new module:

just migrate

Or run migrations manually using the subcommand:

go run cmd/server/main.go migrate

Or using the flag:

go run cmd/server/main.go -migrate

This will:

  • Discover all modules with migrations
  • Run migrations in order
  • Create database tables for your new module

Verify migrations:

# Connect to the database
psql postgres://postgres:postgres@localhost:5432/modulith_demo

# List tables
\dt

# Check migration versions (each module has its own migration tracking)
SELECT * FROM schema_migrations;

You should see tables for your new module (e.g., orders table if you created an order module).

Note: Migrations run automatically when you start the server. The modulith discovers and applies migrations for all registered modules.


Step 9: Test Your Module

9.1 Run Unit Tests

Run tests to verify everything works:

just test

Or run tests for a specific module:

go test ./modules/order/...

9.2 Generate Mocks (if needed)

If you're writing tests that require mocks:

just generate-mocks

This generates mocks for all interfaces in your modules.

9.3 Verify Code Quality

Run the linter to ensure code quality:

just lint

Fix any issues reported by the linter.


Step 10: Run the Server

Now you're ready to run the server with your new module!

Option A: Run with Hot Reload (Recommended for Development)

Run the monolith server with hot reload:

just dev

This will:

  • Start the gRPC server (port 9000 by default, configurable)
  • Start the HTTP gateway (port 8000 by default, configurable)
  • Automatically reload on code changes
  • Monitor changes in .go, .yaml, .env, .proto, and .sql files
  • Run migrations automatically on startup

Option B: Run a Specific Module Standalone

Run your module as a standalone service:

just dev-module order

This runs only the order module with hot reload.

Option C: Build and Run

Build and run without hot reload:

# Build the server
just build

# Run it
./bin/server

Verify Everything Works

10.1 Check Health Endpoints

# Liveness probe
curl http://localhost:8000/livez

# Readiness probe (checks all dependencies)
curl http://localhost:8000/readyz

# WebSocket health
curl http://localhost:8000/healthz/ws

10.2 Check gRPC Service

If you have grpcurl installed:

# List services
grpcurl -plaintext localhost:9000 list

# List methods for your service
grpcurl -plaintext localhost:9000 list order.v1.OrderService

10.3 Check Swagger UI (Development Only)

In development mode, Swagger UI is available at:

http://localhost:8000/swagger-ui/

You can explore and test your API endpoints here.

10.4 Check Logs

The server logs will show:

  • Module initialization
  • Migration status
  • Server startup
  • Request logs

Look for messages like:

Starting application version=...
Module 'order' initialized successfully
✅ Migrations completed successfully
Starting gRPC server port=9000
Starting HTTP Gateway port=8000

Testing Your Module

Unit Tests

Write unit tests for your service and repository layers:

# Run unit tests for your module
go test ./modules/order/... -v

# Run with coverage
go test ./modules/order/... -cover

Integration Tests

For integration tests that require a real database, use testcontainers:

# Run integration tests (requires Docker)
just test-integration

# Or run specific integration tests
go test -v -run Integration ./examples/...

Example Integration Test:

See examples/integration_test_example.go for a complete example showing:

  • Setting up PostgreSQL with testcontainers
  • Running migrations in tests
  • Testing service methods end-to-end
  • Verifying event bus integration
  • Testing repository layer with real database

Key Points:

  • Integration tests should be marked with -run Integration flag
  • Use testing.Short() to skip integration tests in short mode
  • Always clean up test data and containers in defer blocks
  • Use the testutil package for testcontainer setup

Test Coverage

# Generate coverage report
just coverage-report

# View HTML coverage report
just coverage-html

Next Steps

Now that you have a working module, you can:

  1. Customize the Module

    • Edit modules/order/internal/service/service.go to add business logic
    • Update modules/order/internal/repository/repository.go for data access
    • Add more SQL queries in modules/order/internal/db/query/
  2. Add More Methods

    • Update proto/order/v1/order.proto to add new RPC methods
    • Run just proto to regenerate code
    • Implement the methods in your service
  3. Add Database Migrations

    just migrate-create MODULE=order NAME=add_indexes

    This creates new migration files in modules/order/resources/db/migration/

  4. Add Seed Data

    • Edit modules/order/resources/db/seed/001_example_data.sql
    • Run just seed or go run cmd/server/main.go seed
    • Note: The module must implement SeedPath() method in module.go (automatically included when using just new-module)
  5. Add Tests

    • Write unit tests in modules/order/internal/service/service_test.go
    • Write integration tests using testcontainers
  6. Configure Module Settings

    • Edit configs/order.yaml for module-specific configuration
    • Access configuration in your module via registry.Config()

Troubleshooting

Diagnostic Tools

Before troubleshooting, run diagnostic tools:

# Comprehensive environment diagnostics
just doctor

# Validate setup and prerequisites
just validate-setup

Issue: Module not found after registration

Solution: Make sure you:

  1. Added the import for your module in cmd/server/setup/registry.go
  2. Called reg.Register(order.NewModule()) in RegisterModules function
  3. Ran go mod tidy to update dependencies
  4. The module path in the import matches your actual module path

Issue: Migrations fail

Solution:

  • Check database connection string in configs/server.yaml
  • Ensure PostgreSQL is running: docker-compose ps or just doctor
  • Check migration files are valid SQL
  • Verify database container is healthy: docker ps

Issue: Proto generation fails

Solution:

  • Verify buf is installed: which buf or just validate-setup
  • Check buf.yaml and buf.gen.yaml are correct
  • Ensure proto files are valid: buf lint

Issue: SQLC generation fails

Solution:

  • Check sqlc.yaml has correct paths
  • Verify SQL queries are valid
  • Ensure migration files exist for schema

Issue: Server won't start

Solution:

  • Run just doctor to check environment health
  • Check logs for specific errors
  • Verify all required environment variables are set
  • Ensure database is accessible: just doctor will check this
  • Check ports 8000 and 9000 are not in use: just validate-setup shows port status
  • Verify Docker containers are running: docker-compose ps

Issue: Port conflicts

Solution:

  • Run just doctor to identify which ports are in use
  • Stop conflicting services or change ports in configs/server.yaml
  • Check docker-compose ps for running containers

Summary

You've successfully:

  1. ✅ Cloned the repository
  2. ✅ Installed all dependencies
  3. ✅ Configured the project
  4. ✅ Started infrastructure
  5. ✅ Created a new module
  6. ✅ Registered the module
  7. ✅ Generated code (proto + sqlc)
  8. ✅ Run migrations
  9. ✅ Tested the module
  10. ✅ Started the server

Your modulith application is now running with your new module! 🎉

For more information, see: