Skip to content

Overview

A MIC service places its domain core behind executable contracts and explicit dependencies. Python and Node.js share this architecture while keeping their own packages, types, and composition conventions.

MIC provides cross-cutting plumbing. The service continues to own its domain, data, deployment, and performance objectives.

System view

graph TB
    C[HTTP client] --> PA[Python adapter]
    C --> NA[Node.js adapter]
    PA --> R[Route contract]
    NA --> R
    R --> V[Auth and validation]
    V --> CT[Controller / handler]
    CT --> M[Model / domain]
    M --> RP[Service repository]
    RP --> DS[(Storage or broker)]
    M --> OC[Signed client / outbox]
    M --> RS[Canonical result]
    RS --> A[HTTP adapter]
    A --> HR[HTTP response]

The framework does not cross the handler boundary. It translates a native request into the MIC contract, then turns the canonical result into a native response. The same handler can therefore be served by another adapter without rewriting domain behavior.

Three Explicit Owners

The service owns

  • entities, invariants, use cases, and domain error codes;
  • database schemas, migrations, indexes, and specialized queries;
  • event topics, payloads, and versioning policy;
  • configuration, secrets, deployment, and process supervision;
  • capacity objectives, load tests, and production acceptance.

MIC provides

  • static or governed contracts, envelopes, and canonical errors;
  • framework-neutral routes and their HTTP adapters;
  • service-to-service authentication, a signed client, and scope checks;
  • ports and implementations for storage, outbox, queues, cache, locks, and periodic work;
  • logging, metrics, tracing, health, resilience, and testing primitives.

External infrastructure owns

  • the HTTP server and process lifecycle;
  • databases, brokers, caches, observability backends, and identity providers;
  • scheduling, hard termination, retention, backups, and recovery.

A MIC adapter connects these systems to the service. It does not transfer their operation or guarantees to MIC.

Python Composition

The Python implementation is organized around three PyPI distributions that share the mic.* namespace:

  • mic-core for contracts, routes, errors, authentication, the client, and observability;
  • mic-datastore for persistence, outbox, queues, cache, locks, and scheduling;
  • mic-platform for authorization, identity, and rate-limiting policies.

A structured service generally keeps its code under app/modules/<domain>/, separates model, controller, and interface, and assembles dependencies under app/bootstrap/. Its HttpRouteSpec values stay independent from FastAPI or Litestar. See the canonical Python structure and package organization.

Node.js Composition

The Node.js implementation separates responsibilities across npm packages:

  • @mic-struct/mic-core for schemas, routes, errors, authentication, and OpenAPI contracts;
  • @mic-struct/mic-client and @mic-struct/mic-datastore for outbound calls and data primitives;
  • @mic-struct/mic-node-http, @mic-struct/mic-express, @mic-struct/mic-fastify, @mic-struct/mic-hono, and @mic-struct/mic-nest for HTTP adapters;
  • Kafka, NATS, Redis, observability, resilience, and platform packages for optional capabilities.

@mic-struct/create-mic-service deliberately generates a compact starting point in src/ with routes, an example domain, a server, OpenAPI, and an operator CLI. Those files then belong to the application: it can split them into modules without changing route() contracts or handlers. See the Node.js quickstart and the mic-node repository.

Request Lifecycle

  1. The framework adapter receives the native HTTP request.
  2. The authentication gate verifies the token, audience, issuer, and required scopes.
  3. The route contract validates parameters, headers, and body.
  4. The handler receives validated data and explicit dependencies.
  5. The controller orchestrates the use case; the Model applies invariants and calls the required repositories or clients.
  6. A DomainError describes a permanent failure; a TransientError signals that a retry may succeed.
  7. The handler returns a canonical result that the adapter validates and serializes.
  8. Correlation, tracing, and metrics cover the boundary without entering domain rules.

Exact names follow each language, such as StrictSchema in Python and schema.* in Node.js, but the responsibility order remains the same.

What Portability Guarantees

MIC portability means that changing an adapter does not change handler behavior, contracts, or domain tests. It does not mean that Python and Node.js services share source code or a data model.

To evaluate this boundary, test these surfaces separately:

  • the handler with substituted dependencies;
  • the public contract and its errors;
  • every selected adapter through a real HTTP test;
  • the generated OpenAPI document;
  • stores and transports against their real engines before production.

Also review scope and exit strategy to separate foundation guarantees from product-owned responsibilities.