A Go framework for building distributed modular monolith applications powered by NATS.io.
Mono Framework enables building applications as a collection of loosely-coupled modules that communicate via NATS messaging. Start with a single binary monolith for simplicity, then scale horizontally to a distributed cluster when needed—without changing your code. Powered by NATS.io's distributed architecture, your application can seamlessly evolve from a single instance to a highly scalable distributed system.
- Distributed Modular Monolith - Start simple, scale horizontally without code changes
- NATS.io Powered - Built on NATS distributed messaging for high scalability and resilience
- Embedded or External NATS - Run embedded for development, connect to NATS clusters in production
- Event-Driven Communication - Publish/subscribe patterns for inter-module messaging
- Four Service Patterns - Channel, Request-Reply, Queue Group, and Stream Consumer
- JetStream Persistence - Durable messaging with at-least-once delivery guarantees
- Automatic TLS - Let's Encrypt certificates for client connections, obtained and renewed over ACME with no restart
- Lifecycle Management - Automatic dependency resolution and ordered startup/shutdown
- Built-in Middleware - Access logging, audit trails, and request ID injection
- Plugin System - Extensible architecture for custom functionality
| Approach | Development | Deployment | Scaling |
|---|---|---|---|
| Traditional Monolith | Simple | Single binary | Vertical only |
| Microservices | Complex | Many services | Horizontal |
| Distributed Modular Monolith | Simple | Single binary | Horizontal |
Mono Framework gives you the best of both worlds:
- Develop like a monolith: single codebase, simple debugging, no network complexity during development
- Deploy like microservices: run multiple instances behind a load balancer, scale horizontally on demand
- Communicate through NATS: modules use messaging patterns that work identically whether running in one process or distributed across a cluster
go get github.com/go-monolith/monopackage main
import (
"context"
"log"
"time"
"github.com/go-monolith/mono"
)
// HelloModule implements a simple module
type HelloModule struct{}
func (m *HelloModule) Name() string { return "hello" }
func (m *HelloModule) Start(_ context.Context) error { return nil }
func (m *HelloModule) Stop(_ context.Context) error { return nil }
func main() {
// Create application with configuration
app, err := mono.NewMonoApplication(
mono.WithLogLevel(mono.LogLevelInfo),
mono.WithShutdownTimeout(10*time.Second),
)
if err != nil {
log.Fatal(err)
}
// Register module and start
app.Register(&HelloModule{})
if err := app.Start(context.Background()); err != nil {
log.Fatal(err)
}
// Application is now running with embedded NATS server
// Handle shutdown...
app.Stop(context.Background())
}┌───────────────────────────────────────────────────────────────────────┐
│ Application Layer │
│ (Your Modules implementing mono.Module) │
├───────────────────────────────────────────────────────────────────────┤
│ Framework Layer │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────────────┐ │
│ │ ServiceContainer │ │ EventBus │ │ EventRegistry │ │
│ │ (DI & Services) │ │ (Pub/Sub) │ │ (EDA & Consumers) │ │
│ └──────────────────┘ └──────────────────┘ └───────────────────────┘ │
├───────────────────────────────────────────────────────────────────────┤
│ Infrastructure Layer │
│ (NATS.io + JetStream Persistence) │
└───────────────────────────────────────────────────────────────────────┘
│
┌──────────────────────────┼──────────────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Node 1 │◄────────────►│ Node 2 │◄────────────►│ Node 3 │
│(Instance)│ NATS Cluster │(Instance)│ NATS Cluster │(Instance)│
└──────────┘ └──────────┘ └──────────┘
Modules communicate through NATS messaging patterns rather than direct method calls. This enables loose coupling, clear module boundaries, and horizontal scaling—deploy multiple instances that automatically coordinate through the NATS cluster.
| Pattern | Use Case | Latency | Durability |
|---|---|---|---|
| Channel | In-process communication | ~microseconds | None |
| Request-Reply | Synchronous service calls | ~1ms | None |
| Queue Group | Load-balanced async processing | ~1ms | None |
| Stream Consumer | Durable message processing | ~1ms | JetStream |
| Example | Description |
|---|---|
| basic | Hello World module with lifecycle management |
| multi-module | Order system with dependencies and service patterns |
| analytics | Channel services for high-performance in-process communication |
| event-emitter | Event publishing with EventEmitter and EventConsumer |
| auto-tls | Automatic Let's Encrypt certificates for client-to-server connections |
accesslog- HTTP-style access logging for service callsaudit- Security event auditingrequestid- Request ID injection and propagation
fs-jetstream- File storage using JetStream Object Storekv-jetstream- Key-value storage using JetStream KV Store
The framework includes built-in security features:
- Sensitive Data Redaction - Automatic redaction of passwords, tokens, API keys, and credentials from logs
- Audit Logging - Security event tracking with optional hash chaining for tamper detection
- Input Validation - Validation helpers for service handlers
- Automatic TLS (AutoTLS) - ACME certificates for client-to-server NATS connections, renewed in the background
Enable AutoTLS with:
app, err := mono.NewMonoApplication(
mono.WithNATSHost("0.0.0.0"),
mono.WithNATSAutoTLS(types.AutoTLSConfig{
Domains: []string{"nats.example.com"},
Email: "ops@example.com",
CacheDir: "/var/lib/mono/acme",
AcceptTOS: true,
}),
)It serves the ACME http-01 challenge from its own listener on port 80, so the
domain must resolve to this host and port 80 must be reachable. Enabling it
makes TLS mandatory for external clients, which must connect by hostname over
tls://.
Scope: client-to-server connections only. AutoTLS secures the NATS client listener. Route (cluster), gateway, leafnode, websocket and MQTT listeners have their own separate TLS configuration and are not affected, so traffic between cluster nodes stays plaintext unless you configure it yourself — supply an internal CA through a
cluster { tls { ... } }block in a NATS config file. A public ACME certificate is not a good fit for routes: they are peer-to-peer and conventionally use mutual TLS, which needs a client certificate autocert does not issue.
See the AutoTLS example and the AutoTLS design.
For security best practices and vulnerability reporting, see SECURITY.md.
| Resource | Description |
|---|---|
| Official Documentation | Complete framework guide (GitBook format) |
| Quick Start Guide | Get started in 5 minutes |
| Core Concepts | Modules, services, and architecture |
| API Reference | Detailed API documentation |
| Go Documentation | Generated godoc reference |
| Examples | Runnable example applications |
For contributing to the framework, building from source, and running tests, see DEVELOPMENT.md.
See CONTRIBUTING.md for guidelines on how to contribute to this project.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.