# Service Disposal & Cleanup in Parsley

When building robust Go applications, managing the lifecycle of resources such as database connections, file handles, or network clients is critical. Parsley provides built-in support for explicit resource cleanup and automatic service disposal through the `Disposable` interface and resolver shutdown methods.

## The `Disposable` Interface

Any service that requires explicit cleanup when it is no longer needed can implement the `types.Disposable` interface:

```go
type Disposable interface {
    Dispose(ctx context.Context) error
}
```

When services managed by Parsley implement `Dispose(ctx context.Context) error`, the framework can automatically track them and invoke their cleanup routines during application shutdown or scope disposal.

## Singleton Service Disposal: `Resolver.Shutdown`

Singleton services remain valid for the lifetime of the resolver. To clean up all registered singleton services that implement `Disposable`, you can call the `Shutdown` method on the `Resolver` interface:

```go
err := resolver.Shutdown(ctx)
```

### Example

Consider a disposable service registered as a singleton:

```go
type DatabaseClient struct {
    closed bool
}

func (db *DatabaseClient) Dispose(ctx context.Context) error {
    db.closed = true
    return nil
}

func NewDatabaseClient() *DatabaseClient {
    return &DatabaseClient{}
}
```

Registering and shutting down the singleton service:

```go
registry := registration.NewServiceRegistry()
_ = registration.RegisterSingleton(registry, NewDatabaseClient)

resolver := resolving.NewResolver(registry)
ctx := resolving.NewScopedContext(context.Background())

// Resolve the singleton service
client, _ := resolving.ResolveRequiredService[*DatabaseClient](ctx, resolver)

// Shut down the resolver to clean up singletons
err := resolver.Shutdown(ctx)
if err != nil {
    // Handle error (or NewAggregateError if multiple services return errors)
}
```

## Scoped Service Disposal: `DisposeScope`

For services registered with a scoped lifetime, instances are tied to a specific context (such as an HTTP request or task scope). You can dispose of all scoped services associated with a context by calling `DisposeScope`:

```go
err := resolving.DisposeScope(ctx)
```

### Example

```go
registry := registration.NewServiceRegistry()
_ = registration.RegisterScoped(registry, NewDatabaseClient)

resolver := resolving.NewResolver(registry)
ctx := resolving.NewScopedContext(context.Background())

// Resolve scoped service
client, _ := resolving.ResolveRequiredService[*DatabaseClient](ctx, resolver)

// Dispose the scope when the request or task completes
err := resolving.DisposeScope(ctx)
```

> **Note:** Transient services are not tracked by the resolver or scope bags and are not automatically disposed by `Shutdown` or `DisposeScope`. Callers are responsible for cleaning up transient instances if needed.

## Disposal Order and Thread Safety

- **Reverse Order of Creation:** When disposing singleton or scoped instances, Parsley automatically tracks instances in an internal container (`InstanceBag`) and disposes them in the **reverse order of their creation**. This ensures that dependencies created earlier outlive their dependents during teardown.
- **Thread Safety:** Service disposal is thread-safe, protecting against concurrent shutdown or scope disposal requests.
- **Error Aggregation:** If multiple services return an error during `Dispose`, Parsley aggregates them using `ParsleyError` in `pkg/types`.

## Automatic Application Shutdown

When bootstrapping applications using `bootstrap.RunParsleyApplication`, Parsley automatically handles the disposal of singleton and scoped services upon application exit, ensuring clean resource teardown without manual `Shutdown` calls.
