> For the complete documentation index, see [llms.txt](https://docs.parsue.io/aceland-unity-packages/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.parsue.io/aceland-unity-packages/core-packages/injection/for-package-authors.md).

# For Package Authors

If you are building a **reusable package** — not the final application — you usually want to *use* dependency injection without *forcing* your consumers to install the full runtime. That is exactly what **`com.aceland.injection.abstractions`** is for.

This page explains the contracts-only layer and the patterns that keep your package decoupled and optional.

***

### Two packages, one namespace

<table><thead><tr><th width="318.4000244140625">Package</th><th width="188.5999755859375">Role</th><th>Contains</th></tr></thead><tbody><tr><td><code>com.aceland.injection.abstractions</code></td><td>Contracts / attributes</td><td>Interfaces, attributes, the <code>InjectionBridge</code>. No container implementation.</td></tr><tr><td><code>com.aceland.injection</code></td><td>Runtime</td><td>The actual container, scopes, source generator and editor tooling.</td></tr></tbody></table>

Both live in the **`AceLand.Injection`** namespace, so a `using AceLand.Injection;` is all a consumer ever needs — whichever package(s) they have installed.

{% hint style="info" %}
**Depend on `abstractions` only.** Your package references the contracts package; the *application* that consumes your package references the runtime. This means your package adds no container code to projects that don't want it.
{% endhint %}

***

### The weak link: `InjectionBridge`

`InjectionBridge` is a static, allocation-light bridge that lets abstraction-only code reach the global container **if it exists** — and degrade gracefully to `false`/`null` if the runtime package is absent.

<table><thead><tr><th width="252">Member</th><th width="125.7999267578125">Returns</th><th>Notes</th></tr></thead><tbody><tr><td><code>IsAvailable</code></td><td><code>bool</code></td><td><code>true</code> once the runtime has wired itself up.</td></tr><tr><td><code>Global</code></td><td><code>IResolver</code></td><td>The live global resolver, or <code>null</code> if unavailable / disposed.</td></tr><tr><td><code>TryResolve&#x3C;T>(out T, object id = null)</code></td><td><code>bool</code></td><td>Safe resolve; <code>false</code> when the runtime is missing.</td></tr><tr><td><code>TryInject(object target)</code></td><td><code>bool</code></td><td>Injects into an existing instance; <code>false</code> when unavailable.</td></tr></tbody></table>

```csharp
using AceLand.Injection;

public sealed class MyOptionalFeature
{
    public void Boot()
    {
        // Works whether or not the app installed the injection runtime.
        if (InjectionBridge.TryResolve<ILogger>(out var logger))
            logger.Log("Injection is available — using the shared logger.");
        else
            FallbackLog("No DI runtime; using a built-in logger.");
    }
}
```

{% hint style="warning" %}
`InjectionBridge` is the **only** safe entry point from an abstractions-only package. Do not call `DI.Resolve<T>()` from such a package — `DI` lives in the runtime package and would create a hard dependency.
{% endhint %}

***

### Shipping installers with your package

Let your package register its own services so consumers get them "for free". Declare a global installer and either mark it `[AutoInstall]` or register it with an assembly attribute.

{% tabs %}
{% tab title="AutoInstall" %}

```csharp
using AceLand.Injection;

// Discovered automatically; runs at global container build.
[AutoInstall(order: -100)] // lower runs earlier
public sealed class MyPackageInstaller : IGlobalInstaller
{
    public void Install(IContainerBuilder builder)
    {
        // Only add what's not already there, so the app can override you.
        builder.RegisterIfMissing<IMyService, MyService>();
    }
}
```

{% endtab %}

{% tab title="Assembly attribute" %}

```csharp
using AceLand.Injection;

// Register without adding an attribute to the installer type itself.
[assembly: InjectionInstaller(typeof(MyPackageInstaller), -100)]

public sealed class MyPackageInstaller : IGlobalInstaller
{
    public void Install(IContainerBuilder builder)
        => builder.RegisterIfMissing<IMyService, MyService>();
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Use **`RegisterIfMissing`** in package installers. It only registers when the contract is absent, so the consuming application can always supply its own implementation without a conflict.
{% endhint %}

***

### Design checklist

* **Reference `abstractions`, never the runtime**, from a reusable package.
* Expose **contracts** (`interface IMyService`) — let the app decide the implementation.
* Reach the container through **`InjectionBridge`**, and always handle the "runtime absent" path.
* Register with **`RegisterIfMissing`** so apps can override your defaults.
* Give installers an explicit **`order`** when they must run before / after others.
* Keep a single `using AceLand.Injection;` — one namespace across both packages.

***

### Best Practices

* Treat DI as **optional** in a package: it should work standalone and light up when the runtime is present.
* Never `throw` because the runtime is missing — branch on `InjectionBridge.IsAvailable`.
* Prefer constructor `[Inject]` on your own types so they get compiled injector plans in apps that have the generator.
* Document which contracts your package registers, and at what `order`, so app authors can override cleanly.
