Docs Embedding Plumego

Embedding Plumego

Plumego is designed to be embedded, not monopolistic.

It does not assume it owns the process.
It does not assume it owns the server lifecycle.
It does not assume it is the only HTTP surface.

This makes Plumego particularly suitable for integration-heavy systems
but only if embedding is done intentionally.

This document explains how to embed Plumego without breaking its guarantees.


What “Embedding” Means

Embedding Plumego means:

  • Plumego is one component of a larger system
  • It coexists with other HTTP handlers, services, or runtimes
  • It does not control process startup or shutdown
  • It operates under externally defined constraints

Common embedding contexts include:

  • Monolithic applications with multiple subsystems
  • API gateways or edge services
  • Existing legacy net/http servers
  • Admin / internal tools mounted alongside other handlers
  • Multi-protocol servers (HTTP + metrics + debug endpoints)

Core Constraint: Plumego Remains an http.Handler

The most important rule:

Plumego must remain a normal http.Handler.

Embedding should never require:

  • Forking Plumego
  • Reaching into internal state
  • Hooking undocumented lifecycle points
  • Sharing internal Context across systems

If embedding requires any of these, the approach is wrong.


Pattern 1: Mounting Plumego Under a Path Prefix

The most common embedding pattern is path-based mounting.

Example:

plumeApp := plumego.New()
// register routes

mux := http.NewServeMux()
mux.Handle("/api/", http.StripPrefix("/api", plumeApp))
mux.Handle("/metrics", metricsHandler)
mux.Handle("/healthz", healthHandler)

In this model:

  • Plumego handles only /api/*
  • Other endpoints remain outside Plumego
  • Routing responsibilities are explicit

Plumego’s internal router is unchanged.


Pattern 2: Embedding Multiple Plumego Apps

You may embed multiple independent Plumego instances in one process.

Example:

publicAPI := buildPublicAPI()
adminAPI  := buildAdminAPI()

mux := http.NewServeMux()
mux.Handle("/api/", publicAPI)
mux.Handle("/admin/", adminAPI)

Each Plumego app:

  • Has its own middleware chain
  • Has its own routing table
  • Has its own configuration slice

This is often cleaner than overloading a single app.


Pattern 3: Plumego Inside a Larger Router

Sometimes Plumego is just one branch of a more complex router.

Example scenarios:

  • Host-based routing
  • Version-based routing
  • Protocol multiplexing

Example:

func rootRouter(plume http.Handler, legacy http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.HasPrefix(r.URL.Path, "/v2/") {
			plume.ServeHTTP(w, r)
			return
		}
		legacy.ServeHTTP(w, r)
	})
}

Plumego remains unaware of the larger routing logic.


Pattern 4: Embedding in Long-Lived Processes

Plumego works well in processes that:

  • Run background workers
  • Maintain in-memory state
  • Host multiple protocols

Important rules:

  • Do not share Plumego Context outside request scope
  • Do not assume request lifecycle aligns with process lifecycle
  • Keep shutdown orchestration outside Plumego

Plumego handles requests — nothing more.


Configuration Boundaries When Embedding

Embedding often exposes configuration pitfalls.

Best practices:

  • Slice configuration per embedded app
  • Avoid global configuration structs
  • Inject only what each Plumego app needs

Example:

publicCfg := cfg.PublicAPI
adminCfg  := cfg.AdminAPI

Do not let embedding collapse configuration boundaries.


Logging and Observability Integration

When embedding Plumego:

  • Prefer shared logging infrastructure
  • Inject loggers into middleware
  • Use consistent trace/Request IDs across handlers

Plumego should participate in observability,
not redefine it.

Avoid per-app logging systems unless isolation is required.


Authentication and Security Boundaries

Embedding multiple subsystems raises security questions.

Rules of thumb:

  • Do not rely on path prefixes alone for security
  • Use explicit middleware per embedded app
  • Assume nothing about upstream handlers

Each Plumego app should enforce its own security invariants.


Lifecycle and Shutdown Coordination

Because Plumego does not own the server lifecycle:

  • Shutdown signals must be handled externally
  • Server shutdown must be coordinated at the process level
  • Plumego apps do not need to know about shutdown unless they hold resources

Use standard Go patterns (context.Context, http.Server.Shutdown).


What Not to Do When Embedding

Do Not Share Plumego Context Across Systems

Context is request-scoped and framework-specific.


Do Not Inject External State into Context

Embedding should not turn Context into a global bus.


Do Not Assume Execution Order Across Handlers

Only the HTTP server defines dispatch order.


Do Not Hide Plumego Behind Magical Adapters

If you cannot see where Plumego is mounted,
debugging will suffer.


Testing Embedded Plumego Apps

Testing strategies:

  • Test Plumego apps standalone with httptest
  • Test embedding routers separately
  • Add a small number of integration tests for full wiring

Avoid testing everything through the top-level server.


Maintenance and Evolution

Embedding Plumego cleanly provides long-term benefits:

  • Subsystems can evolve independently
  • Routing changes are localized
  • Migration paths remain open
  • Framework upgrades are low-risk

Poor embedding choices make refactoring painful.


Summary

When embedding Plumego:

  • Treat it as a normal http.Handler
  • Keep boundaries explicit
  • Mount, don’t merge
  • Coordinate lifecycle externally
  • Preserve request-scoped guarantees

Plumego is designed to fit into larger systems —
but only if you let it remain what it is.


Next

A natural follow-up topic is:

Advanced / Performance Considerations

This explores how embedding and scale interact with throughput,
latency, and resource usage.