HomeiOS DevelopmentThe summary Vapor service manufacturing unit design sample

The summary Vapor service manufacturing unit design sample



I’ve written a number of articles about manufacturing unit design patterns on my weblog and this time I might like to speak a couple of particular one, which you’ll be able to encounter in case you work with Vapor. Here is a bit recap about my manufacturing unit design sample weblog posts, all written in Swift:




Now let’s dive in to the “Fluent sample”. With the intention to perceive this structure, first we must always study the associated Swift packages first. There’s the FluentKit library and several other Fluent database driver implementations (SQLite, PostgreSQL, MySQL, and so forth.), all based mostly on the FluentKit product. Additionally there may be one bundle that connects Fluent with Vapor, this one is just known as: Fluent. 📀


  • FluentKit – comprises the summary interface (with out Vapor, utilizing SwiftNIO)
  • Fluent[xy]Driver – comprises the implementation outlined in FluentKit
  • Fluent – connects FluentKit with Vapor, by extending Vapor


That is the bottom construction, the FluentKit library gives the next summary interfaces, which it’s important to implement if you wish to create your personal driver implementation. Sadly you will not be capable to discover correct documentation for these interfaces, so I am going to clarify them a bit:


  • Database – Question execution and transaction associated capabilities
  • DatabaseContext – Holds the config, logger, occasion loop, historical past and web page measurement restrict

  • DatabaseDriver – A manufacturing unit interface to create and shutdown Database situations
  • DatabaseID – A singular ID to retailer database configs, drivers and situations
  • DatabaseError – A generic database associated error protocol
  • DatabaseConfiguration – A protocol to create DatabaseDriver objects
  • DatabaseConfigurationFactory – A box-like object to cover driver associated stuff
  • Databases – Shared config, driver and operating occasion storage


As you possibly can see there are lots of protocols concerned on this structure, however I am going to attempt to stroll you thru your entire driver creation move and hopefully you can perceive how the items are associated, and the way can construct your personal drivers and even Vapor parts based mostly on this.


Fluent is written as a service for Vapor utilizing the underlying shared storage object, that is what shops a reference to the Databases occasion. This object has two hash maps, for storing configurations and operating driver situations utilizing the DatabaseID as a key for each. 🔑


Whenever you ask for a driver, the Databases object will verify if that driver exists, if sure, it will merely return it and story over. The attention-grabbing half occurs when the driving force doesn’t exists but within the Databases storage. First the system will verify for a pre-registered driver implementation.


app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite)


This line above registers a brand new driver configuration for the shared Databases. The .sqlite() methodology is a static operate on the DatabaseConfigurationFactory which creates a brand new SQLite particular configuration and hides it utilizing the init(make:) name. The SQLite associated configuration implements the DatabaseConfiguration protocol, so it may be used as a legitimate config when the system creates the precise database context.


The config object can be chargeable for creating the precise driver object utilizing the Databases object if wanted. At this level we have got a configuration and a driver occasion registered within the databases storage. What occurs if somebody asks for a database occasion?


Relying on the context, you possibly can ask for a Database implementation by means of the app.db or req.db properties. That is outlined within the FluentProvider code and behind the scenes every part will be traced again to the Databases class. Because you solely wish to have a single shared storage for all of the drivers, however you additionally wish to keep away from the singleton sample, you must hook this service as much as the Utility class. That is how the Vapor people did it anyway. 🤓


let db: Database = req.db
let db: Database = req.db(.sqlite)

let db: Database = app.db
let db: Database = app.db(.sqlite)


Whenever you ask for a database, or a database with an express identifier, you’re primarily calling a make methodology contained in the Databases class, which goes search for a registered configuration and a driver implementation utilizing the hashes and it will name the driving force’s make methodology and cross across the logger, the occasion loop and the present database configuration as a database context object.


We are able to say that after you ask for an summary Database driver, a brand new DatabaseDriver occasion reference (related to a given DatabaseID) shall be saved contained in the Databases class and it will at all times make you a brand new Database reference with the present DatabaseContext. If the driving force already exists, then it will be reused, however you continue to get new Database references (with the related context) each time. So, it is very important be aware that there’s just one DatabaseDriver occasion per configuration / database identifier, however it may well create a number of Database objects. 🤔


Okay, I do know, it is fairly sophisticated, however this is an oversimplified model in Swift:



remaining class Databases {
    var configs: [DatabaseID: DatabaseConfiguration] = [:]
    var drivers: [DatabaseID: DatabaseDriver] = [:]

    func make(
        _ id: DatabaseID,
        logger: Logger,
        on eventLoop: EventLoop
    ) -> Database {
        let config = configs[id]!

        if drivers[id] == nil {
            drivers[id] = config.make(self)
        }
        let context = DatabaseContext(config, logger, eventLoop)
        return drivers[id]!.make(context)
    }

    func use(_ config: DatabaseConfiguration, for id: DatabaseID) {
        configs[id] = config
    }
}


And the Vapor service extension may very well be interpreted considerably like this:


extension Utility {

    var databases: Databases {
        get {
            if storage[DatabasesKey.self] == nil {
                storage[DatabasesKey.self] = .init()
            }
            return storage[DatabasesKey.self]
        }
        set {
            self.storage[MyConfigurationKey.self] = newValue
        }
    }

    var db: Database {
        databases.make(
            .default, 
            logger: logger, 
            eventLoop: eventLoopGroup.subsequent()
        )
    }
}



You’ll be able to apply the identical ideas and create an extension over the Request object to entry a Database occasion. In fact there’s much more taking place underneath the hood, however the goal of this text is to get a fundamental overview of this sample, so I am not going into these particulars now. 🙃


Actually I actually like this strategy, as a result of it is elegant and it may well fully cover driver particular particulars by means of these abstractions. I adopted the very same ideas once I created the Liquid file storage driver for Vapor and discovered so much through the course of. Though, you must be aware that not every part is an efficient candidate for being carried out an “summary Vapor service manufacturing unit” design sample (or no matter we name this strategy). Anyway, I actually hope that this fast tutorial will assist you to create your personal Vapor parts, if wanted. 🤷‍♂️


RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments