As for your question, let me rewrite my example in more concrete terms (as I see myself now that it's a bit confusing), it should get more obvious.
So I have an abstract entity protocol:
public protocol Entita2Entity: Codable {
// ...
static var storage: any Entita2Storage { get set }
// ...
}
Entita2Storage
doesn't really have anything of interest, it's just a protocol for abstract storage:
public protocol Entita2Storage {
func begin() async throws -> any Entita2Transaction
func load(by key: Bytes, within transaction: (any Entita2Transaction)?) async throws -> Bytes?
// etc
}
Somewhere else there's my FDBSwift
package with FoundationDB connector, which has a protocol
public protocol FDBConnector {
/// basically all methods like `connect`, `begin`, `get`, `set` etc
}
and an actual implementation:
public extension FDB {
final class Connector: FDBConnector { ... }
}
Back to Entita2
, there's an additional Entita2FDB
package, which inherits and extends both Entita2Entity
and Entita2Storage
:
public protocol Entita2FDBEntity: Entita2Entity {
// ...
static var storage: any Entita2FDBStorage { get set }
// ...
}
public protocol Entita2FDBStorage: Entita2Storage, FDBConnector {
// FDB-related stuff
}
Originally it was actually done using associatedtype
, but back then there was only actual FDB
connector class, a respective protocol didn't exist, and therefore it was super easy to provide entities with storage:
public extension E2FDBEntity {
static var storage = App.current.fdb
}
And this is the most interesting part: there's an App
class with all configs, resources, caches, storages etc for the runtime, and originally it looked like this:
final class App {
// ...
public static var current: App!
// ...
public let fdb: FDB
// ...
}
Now I want the app to have not old FDB
, but rather an any Entita2FDBStorage
, which I can easily replace with a mock implementation for tests. Since some Entita2FDBStorage
would mean that the App
becomes App<Storage: Entita2FDBStorage>
, I can't have static current
instance anymore (I keep forgetting why, but ok), and because the App
is defined in a library module, and executable is different module (like Run
in Vapor), everything falls apart.
Speaking of Vapor. I don't like the idea of passing the Application
into every route action and Fluent
model action, and I doubt that it will work anyway without any
existential. I'm not saying it's wrong, I just like my approach (with singleton dependency container) more, that's all. I realize it's unsafe, not clean and against all mortal sins of app design, but it's handy.
Ref links (pre-any
implementations):
- GitHub - kirilltitov/FDBSwift: FoundationDB client for Swift
- GitHub - 1711-Games/Entita2
- GitHub - 1711-Games/Entita2FDB