orlandos-nl / MongoKitten

Native MongoDB driver for Swift, written in Swift

Home Page:https://orlandos.nl/docs/mongokitten/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Use Protocols for return types

Juice805 opened this issue · comments

Is your feature request related to a problem? Please describe.
When using MongoKitten I find it difficult to unit test as it requires a remote connection to mongodb instance or lots of boilerplate and duplication of definitions in tests.

Describe the solution you'd like
If MongoKitten returned objects conforming to Cluster, Database etc. protocols and returned promises with protocol values instead of explicit return types it would allow for easy adoption by test types making unit testing much easier and more reliable.

Describe alternatives you've considered
Writing my own protocols in my framework, conforming MongoKittens collections, databases, replies etc. to them.

Hey @Juice805, thanks for your idea!

@Joannis and I discussed this, but we don't feel that this should be a part of MongoKitten itself, as MongoKitten is just a database driver. As you described you could of course just write your own protocols and conform MongoKittens types to your protocols. If you feel that a larger part of the community would benefit from such a feature, I would suggest creating a separate package that users can include as a dependency in their projects.

A change like this (especially changing return types of existing methods) would also be a breaking change and not possible until the next major version of MongoKitten.

I'm closing this issue for now, but if you decide to make this into a separate package, please let us know: we would be happy to give your package a spot in the readme for discoverability.

we don't feel that this should be a part of MongoKitten itself, as MongoKitten is just a database driver.

This doesn't seem like much of an argument. On the contrary, I would argue it's even more important for network dependent frameworks to be testable on the framework boundary. Every adopter of this package should be writing unit tests, and as written each of them will have to write a lot of boilerplate to make any unit testing feasible and reliable. Testability should be one of the priorities!

At minimum MeowVapor (The context I'm moving towards from raw MongoKitten) should provide protocols as it's what the Vapor Style Guide recommends this for the same reasons, and they implement their own components this way. I will open an issue there instead, but truthfully the benefits are so large that it should just be pulled into this package. Plus, not everyone will want to use the Meow layer.

A change like this (especially changing return types of existing methods) would also be a breaking change and not possible until the next major version of MongoKitten.

Better late than never!

Before I go into the reasoning, I think it's interesting to mention that I am one of the creators of what is now Vapor 3..

I'm well aware of the style guide and recommended practices. With regards to Dependency Injection, I think you're spot on with regards to the importance of Dependency Injections and testing, but I never intended for packages to be complicit with the dependency injection pattern as such a fundamental level.

Reason 1 - Performance

My official title at the Vapor company "Qutheory" was performance engineer. I was responsible not only for creating the Vapor 3 framework, but also ensuring it's performance is the best it can reasonably be. And for this reason, returning protocols instead of concrete types is one of the first things I'd like to prevent.

By returning a protocol you're preventing a tonne of compiler optimizations from kicking in as well as forcing the compiler to access witness tables and other shenanigans to figure out where to send a function call or access a property. If we'd return protocols everywhere the performance would get a huge hit and the maintainability of the driver would suffer.

Instead, as is practice in Vapor, you can create an objectively correct driver and conform it to protocols with associatedtypes, this'll reduce the performance impact and improve maintainability.

Reason 2 - Non-Vapor use cases

Vapor is my framework of choice, almost all intented features made it to release. It's the ideal framework for me. But I do want and need to acknowledge that not everyone likes to use Vapor. If only because IBM offers commercial support. Their choice for a web-framework doesn't need to force them to use other database drivers, cryptographic function implementations and other features that I've got readily available.

Tailoring a driver to a specific framework is not my vision, and it's not Vapor's vision either. However, that doesn't mean we don't provide better support for Vapor users. You can see that in our support for Meow through MeowVapor, where there's a lack of support by us for Kitura. And the same goes for Fluent.

Reason 3 - Protocols

As you described yourself, protocols can be defined in your application and then "forced" upon MongoKitten types. This is one of the most amazing and interesting features of Swift, and definitely useful for Dependency Injection with Vapor Services. If you really strongly believe that MongoKitten needs to conform to certain abstract protocols, you can create a package that does it for you.

import BSON

protocol DatabaseType {
   associatedtype DataType
}

public protocol Storable: DatabaseType {
   func store(_ document: DataType) -> Future<Void>
}

extension MongoKitten.Collection: DocumentStorable {
    public func store(_ document: Document) -> Future<Void> {
        return insert(document).map { _ in }
    }
}

func testStoring<DatabaseCollection: Storable>(to collection: DatabaseCollection) throws where Database.DataType == Document {
   try collection.store(Document()).wait()
}

Finally

I concur that this is an important feature to have, testability. But we never got to thinking out a matching strategy for Meow. I do however believe that MongoKitten should stay a database driver, not an ORM. So I'd like to protect the scope of MongoKitten from this feature.

If you feel you've got a good idea for testing Meow projects, I'd like to invite you to discuss that in the Meow project issues or with us on slack. We can end up making it a separate MeowTesting package with your inspiration or maybe even support it in Meow itself if there ends up being an agreement in API.

Thanks for the detailed response!

Before I go into the reasoning, I think it's interesting to mention that I am one of the creators of what is now Vapor 3..

good to know

Reason 1 - Performance

👍 This seems reasonable

Reason 2 - Non-Vapor use cases

Tailoring a driver to a specific framework is not my vision, and it's not Vapor's vision either.

This request was in no way tailored to Vapor. That was just the context I was using it in as an example. These protocols would be beneficial for any adopters.

In fact, the way I'm using the framework, Vapor does not interact with MongoKitten at all. I have a driver which abstracts away Mongo and other backend frameworks. My driver is the interface with Vapor, but I still need a good way to test my driver without making actual backend requests.

Reason 3 - Protocols

If you really strongly believe that MongoKitten needs to conform to certain abstract protocols, you can create a package that does it for you.

This is essentially what I've done already locally, and was the stated alternative.

Finally

I concur that this is an important feature to have, testability. But we never got to thinking out a matching strategy for Meow. I do however believe that MongoKitten should stay a database driver, not an ORM. So I'd like to protect the scope of MongoKitten from this feature.

It's not clear to me how this request would turn MongoKitten into an ORM?