# Contributing to modrun
## What modrun is
modrun is a **modular application composer for Tokio**: domain [`Module`](src/module.rs)s,
constructor injection, explicit module boundaries, and coordinated lifecycle — wired once
at the composition root. It is **not** a general-purpose DI container or a live runtime
with inter-module messaging.
## Public model (stability)
The stable surface is intentionally small:
| Register constructors | `provide` / `provide_result` / `provide_async` / `provide_result_async` |
| Pre-built values | `supply` |
| Graph roots | `invoke` / `invoke_async` |
| Domain modules | `Module` + `Module::builder("name")` + `provide_private` |
| Value groups | `Group<T>` + `provide_group*` / `supply_group` / `init_group` / `require_group` |
| Lifecycle | `Lifecycle`, [`Hook`](src/lifecycle.rs), `task` / `task_with` |
| Run / stop | `ModrunBuilder::run` / `start`, `RunningApp::stop`, `Shutdowner` |
[`Hook`](src/lifecycle.rs) trait methods may only gain **default** implementations in
minor releases. Constructor and invoker arity stays capped at **eight** parameters; pack
extra deps in a struct.
MSRV is **1.85** (edition 2024), tracked in `Cargo.toml` and CI.
**Application-facing API is stable since 1.0.0.**
## Semver (since 1.0.0)
| **Patch** | Docs, diagnostics, tracing fields, non-breaking bug fixes |
| **Minor** | New `Error` variants (`Error` is `#[non_exhaustive]`), new `Hook` methods with **default** impls only, new optional builder knobs that do not change existing call sites |
| **Major** | Remove or rename public items, change constructor/invoker arity cap (currently **eight**), expose service locator or string-keyed bindings |
**Not covered by stability:** `modrun::__wiring`, `#[doc(hidden)]` builder methods (`provide_dyn`, …),
and any `pub(crate)` internals.
## Performance expectations
Startup cost is dominated by **your** constructors (I/O, parsing), not the framework.
modrun validates the graph once, constructs singletons lazily along a precomputed wave
order, runs invokers, then lifecycle hooks — and drops the container. There is no
runtime lookup after build.
Documented patterns that keep overhead low: `Arc<T>` for shared heavy deps,
`Arc<Group<T>>` / `Group<Arc<T>>` for large groups, shallow modules, no `dot_graph()`
on hot paths, and `default-features = false` when you do not need `logging` / `signal`.
See README「Performance」/「性能」.
## Application code vs wrapper authors
**Application code** should register plain functions:
```rust
Modrun::builder()
.provide(new_config)
.provide(new_server)
.invoke(boot)
```
**Wrapper libraries** that re-export modrun wiring use the hidden [`__wiring`](src/lib.rs)
module and `#[doc(hidden)]` builder methods. These are omitted from docs.rs navigation
and are not part of the application-facing stability promise:
```rust
use modrun::__wiring::{DynProvider, InvokeFn, ProviderFn};
let provider: DynProvider = my_ctor.into_provider();
builder.provide_dyn(provider).invoke_dyn(my_boot.into_invoke());
```
Available in `modrun::__wiring`:
* `ProviderFn`, `FallibleProviderFn`, `AsyncProviderFn`, `FallibleAsyncProviderFn`, `ProviderMarker`
* `InvokeFn`, `AsyncInvokeFn`
* `DynProvider`, `DynInvoker`
Builder: `provide_dyn`, `invoke_dyn`, `provide_group_dyn` (+ `_private` variants on `Module`),
all `#[doc(hidden)]`. There are no `*_mut` builder methods — chain on `self` instead.
## Deliberately rejected
We will not add APIs for:
* Property or field injection
* Runtime string tokens / named bindings (`"primary"` vs `"replica"`) — use newtypes
* Annotations, derives, or auto-scanning
* Macro-generated entire containers
* Service locator (`get<T>()` after build)
* Global singleton registries
* `fx.Populate`, `fx.Annotate`, or `fx.Replace` equivalents
* First-class `decorate` — use [wrapper constructors](examples/wrap.rs) instead
* Module-to-module event buses (not in scope today)
Internal `Container::get` exists **only** during build (`pub(crate)`); it must never become public.
## Adding features
Before proposing new wiring APIs, ask:
1. Can a plain constructor or `Module` scope express it already?
2. Does it push modrun toward reflection-style DI instead of Rust constructor injection?
3. Does it expand the method matrix (sync/async/fallible × public/private × group × dyn)?
Prefer documentation and examples over new registration verbs. See [ROADMAP.md](ROADMAP.md)
for current priorities.
## Development
```bash
cargo test
cargo test --doc
cargo run --example basic
```
Please run tests before opening a PR. Commit messages use Conventional Commits in Chinese
(项目惯例).