import NeedleFoundation

protocol FetchAndSaveDependency: Dependency {
    var database: Database { get }
    var httpClient: HttpClient { get }
}

class FetchAndSave: Component<FetchAndSaveDependency> {
    func run(userId: Int) async throws {
        let api: HttpClient = dependency.httpClient
        let db: Database = dependency.database

        let user = try await api.fetch(userId: userId)
        try await db.save(user: user)
    }
}

In Swift, we generally reach for DI frameworks to solve this. DI frameworks can resolve at either compile time or runtime based on the library, generally runtime-based DI has less boilerplate, with the obvious downside of pushing errors to runtime.

This method of dependency management does not compose with other effects at all though. We can't pull dependencies "out" of the same type that allows us to resolve things like async, errors, or nondeterminism. These all require separate syntax.