Tuesday, February 7, 2023
Learning Code
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
Learning Code
No Result
View All Result
Home Swift

The repository pattern for Vapor 4

learningcode_x1mckf by learningcode_x1mckf
September 9, 2022
in Swift
0
The repository pattern for Vapor 4
74
SHARES
1.2k
VIEWS
Share on FacebookShare on Twitter


You might also like

The abstract Vapor service factory design pattern

SwiftNIO tutorial – The echo server

Introducing – Vapor cheatsheet – The.Swift.Dev.

Fluent is actually damaged


The extra I take advantage of the Fluent ORM framework the extra I understand how laborious it’s to work with it. I am speaking a few explicit design difficulty that I additionally talked about within the future of server side Swift article. I actually don’t love the concept of property wrappers and summary database fashions.


What’s the issue with the present database mannequin abstraction? To begin with, the elective ID property is complicated. For instance you do not have to offer an identifier whenever you insert a file, it may be an nil worth and the ORM system can create a novel identifier (underneath the hood utilizing a generator) for you. So why do we now have an id for create operations in any respect? Sure, you may say that it’s attainable to specify a customized identifier, however actually what number of occasions do we’d like that? If you wish to determine a file that is going to be one thing like a key, not an id subject. 🙃


Additionally this elective property may cause another points, when utilizing fluent you may require an id, which is a throwing operation, alternatively you may unwrap the elective property in case you’re positive that the identifier already exists, however this isn’t a protected strategy in any respect.


My different difficulty is expounded to initializers, in case you outline a customized mannequin you at all times have to offer an empty init() methodology for it, in any other case the compiler will complain, as a result of fashions need to be courses. BUT WHY? IMHO the explanation pertains to this difficulty: you may question the database fashions utilizing the mannequin itself. So the mannequin acts like a repository that you need to use to question the fields, and it additionally represents the the file itself. Is not this towards the clear ideas? 🤔


Okay, one final thing. Property wrappers, subject keys and migrations. The core members at Vapor instructed us that this strategy will present a protected technique to question my fashions and I can ensure that subject keys will not be tousled, however I am really combating versioning on this case. I needed to introduce a v1, v2, vN construction each for the sphere keys and the migration, which really feels a bit worse than utilizing uncooked strings. It’s over-complicated for positive, and it feels just like the schema definition is blended up with the precise question mechanism and the mannequin layer as effectively.


Sorry of us, I actually recognize the trouble that you have put into Fluent, however these points are actual and I do know which you could repair them on the long run and make the developer expertise quite a bit higher.


How one can make Fluent a bit higher?


On the brief time period I am making an attempt to repair these points and happily there’s a good strategy to separate the question mechanism from the mannequin layer. It’s referred to as the repository sample and I might like to provide an enormous credit score to 0xTim once more, as a result of he made a cool reply on StackOverlow about this matter.


Anyway, the primary concept is that you simply wrap the Request object right into a customized repository, it is normally a struct, then you definitely solely name database associated queries inside this particular object. If we check out on the default venture template (you may generate one through the use of the vapor toolbox), we are able to simply create a brand new repository for the Todo fashions.


import Vapor
import Fluent

struct TodoRepository 
    var req: Request
    
    
    init(req: Request) 
        self.req = req
    
    
    
    func question() -> QueryBuilder<Todo> 
        Todo.question(on: req.db)
    
    
    
    func question(_ id: Todo.IDValue) -> QueryBuilder<Todo> 
        question().filter(.$id == id)
    
    
    
    func question(_ ids: [Todo.IDValue]) -> QueryBuilder<Todo> 
        question().filter(.$id ~~ ids)
    

    
    func listing() async throws -> [Todo] 
        strive await question().all()
    
    
    
    func get(_ id: Todo.IDValue) async throws -> Todo? 
        strive await get([id]).first
    

    
    func get(_ ids: [Todo.IDValue]) async throws -> [Todo] 
        strive await question(ids).all()
    

    
    func create(_ mannequin: Todo) async throws -> Todo 
        strive await mannequin.create(on: req.db)
        return mannequin
    
    
    
    func replace(_ mannequin: Todo) async throws -> Todo 
        strive await mannequin.replace(on: req.db)
        return mannequin
    

    
    func delete(_ id: Todo.IDValue) async throws 
        strive await delete([id])
    

    
    func delete(_ ids: [Todo.IDValue]) async throws 
        strive await question(ids).delete()
    


That is how we’re can manipulate Todo fashions, any more you do not have to make use of the static strategies on the mannequin itself, however you need to use an occasion of the repository to change your database rows. The repository will be hooked as much as the Request object through the use of a standard sample. The most straightforward means is to return a service each time you want it.


import Vapor

extension Request 
    
    var todo: TodoRepository 
        .init(req: self)
    


After all this can be a very fundamental resolution and it pollutes the namespace underneath the Request object, I imply, when you’ve got a number of repositories this generally is a downside, however first let me present you learn how to refactor the controller through the use of this easy methodology. 🤓


import Vapor

struct TodoController: RouteCollection 

    func boot(routes: RoutesBuilder) throws 
        let todos = routes.grouped("todos")
        todos.get(use: index)
        todos.put up(use: create)
        todos.group(":todoID")  todo in
            todo.delete(use: delete)
        
    

    func index(req: Request) async throws -> [Todo] 
        strive await req.todo.listing()
    

    func create(req: Request) async throws -> Todo 
        let todo = strive req.content material.decode(Todo.self)
        return strive await req.todo.create(todo)
    

    func delete(req: Request) async throws -> HTTPStatus 
        guard let id = req.parameters.get("todoID", as: Todo.IDValue.self) else 
            throw Abort(.notFound)
        
        strive await req.todo.delete(id)
        return .okay
    


As you may see this fashion we had been capable of eradicate the Fluent dependency from the controller, and we are able to merely name the suitable methodology utilizing the repository occasion. Nonetheless if you wish to unit check the controller it isn’t attainable to mock the repository, so we now have to determine one thing about that difficulty. First we’d like some new protocols.


public protocol Repository 
    init(_ req: Request)


public protocol TodoRepository: Repository 
    func question() -> QueryBuilder<Todo>
    func question(_ id: Todo.IDValue) -> QueryBuilder<Todo>
    func question(_ ids: [Todo.IDValue]) -> QueryBuilder<Todo>
    func listing() async throws -> [Todo]
    func get(_ ids: [Todo.IDValue]) async throws -> [Todo]
    func get(_ id: Todo.IDValue) async throws -> Todo?
    func create(_ mannequin: Todo) async throws -> Todo
    func replace(_ mannequin: Todo) async throws -> Todo
    func delete(_ ids: [Todo.IDValue]) async throws
    func delete(_ id: Todo.IDValue) async throws


Subsequent we will outline a shared repository registry utilizing the Software extension. This registry will enable us to register repositories for given identifiers, we’ll use the RepositoryId struct for this goal. The RepositoryRegistry will be capable to return a manufacturing facility occasion with a reference to the required request and registry service, this fashion we’re going to have the ability to create an precise Repository based mostly on the identifier. After all this entire ceremony will be averted, however I wished to provide you with a generic resolution to retailer repositories underneath the req.repository namespace. 😅


public struct RepositoryId: Hashable, Codable 

    public let string: String
    
    public init(_ string: String) 
        self.string = string
    


public last class RepositoryRegistry 

    personal let app: Software
    personal var builders: [RepositoryId: ((Request) -> Repository)]

    fileprivate init(_ app: Software) 
        self.app = app
        self.builders = [:]
    

    fileprivate func builder(_ req: Request) -> RepositoryFactory 
        .init(req, self)
    
    
    fileprivate func make(_ id: RepositoryId, _ req: Request) -> Repository 
        guard let builder = builders[id] else 
            fatalError("Repository for id `(id.string)` shouldn't be configured.")
        
        return builder(req)
    
    
    public func register(_ id: RepositoryId, _ builder: @escaping (Request) -> Repository) 
        builders[id] = builder
    


public struct RepositoryFactory 
    personal var registry: RepositoryRegistry
    personal var req: Request
    
    fileprivate init(_ req: Request, _ registry: RepositoryRegistry) 
        self.req = req
        self.registry = registry
    

    public func make(_ id: RepositoryId) -> Repository 
        registry.make(id, req)
    


public extension Software 

    personal struct Key: StorageKey 
        typealias Worth = RepositoryRegistry
    
    
    var repositories: RepositoryRegistry 
        if storage[Key.self] == nil 
            storage[Key.self] = .init(self)
        
        return storage[Key.self]!
    


public extension Request 
    
    var repositories: RepositoryFactory 
        utility.repositories.builder(self)
    


As a developer you simply need to provide you with a brand new distinctive identifier and prolong the RepositoryFactory along with your getter to your personal repository sort.


public extension RepositoryId 
    static let todo = RepositoryId("todo")


public extension RepositoryFactory 

    var todo: TodoRepository 
        guard let outcome = make(.todo) as? TodoRepository else 
            fatalError("Todo repository shouldn't be configured")
        
        return outcome
    


We will now register the FluentTodoRepository object, we simply need to rename the unique TodoRepository struct and conform to the protocol as a substitute.



public struct FluentTodoRepository: TodoRepository 
    var req: Request
    
    public init(_ req: Request) 
        self.req = req
    
    
    func question() -> QueryBuilder<Todo> 
        Todo.question(on: req.db)
    

    



app.repositories.register(.todo)  req in
    FluentTodoRepository(req)


We’re going to have the ability to get the repository by way of the req.repositories.todo property. You do not have to vary the rest contained in the controller file.


import Vapor

struct TodoController: RouteCollection 

    func boot(routes: RoutesBuilder) throws 
        let todos = routes.grouped("todos")
        todos.get(use: index)
        todos.put up(use: create)
        todos.group(":todoID")  todo in
            todo.delete(use: delete)
        
    

    func index(req: Request) async throws -> [Todo] 
        strive await req.repositories.todo.listing()
    

    func create(req: Request) async throws -> Todo 
        let todo = strive req.content material.decode(Todo.self)
        return strive await req.repositories.todo.create(todo)
    

    func delete(req: Request) async throws -> HTTPStatus 
        guard let id = req.parameters.get("todoID", as: Todo.IDValue.self) else 
            throw Abort(.notFound)
        
        strive await req.repositories.todo.delete(id)
        return .okay
    


The very best a part of this strategy is which you could merely exchange the FluentTodoRepository with a MockTodoRepository for testing functions. I additionally like the truth that we do not pollute the req.* namespace, however each single repository has its personal variable underneath the repositories key.


You’ll be able to provide you with a generic DatabaseRepository protocol with an related database Mannequin sort, then you can implement some fundamental options as a protocol extension for the Fluent fashions. I am utilizing this strategy and I am fairly pleased with it to date, what do you assume? Ought to the Vapor core crew add higher help for repositories? Let me know on Twitter. ☺️






Source link

Share30Tweet19
learningcode_x1mckf

learningcode_x1mckf

Recommended For You

The abstract Vapor service factory design pattern

by learningcode_x1mckf
February 1, 2023
0
The abstract Vapor service factory design pattern

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...

Read more

SwiftNIO tutorial – The echo server

by learningcode_x1mckf
January 27, 2023
0
SwiftNIO tutorial – The echo server

Intoducing SwiftNIO In the event you used a excessive degree net framework, corresponding to Vapor, up to now, you would possibly had some interplay with occasion loops...

Read more

Introducing – Vapor cheatsheet – The.Swift.Dev.

by learningcode_x1mckf
January 23, 2023
0
Introducing – Vapor cheatsheet – The.Swift.Dev.

Out there on Gumroad Thanks for supporting my work by buying the cheatsheet. 🙏 Download now A whole Vapor framework reference for novices. greater than...

Read more

iCloud Programming Tutorial for iOS: An Introduction

by learningcode_x1mckf
January 18, 2023
0
iCloud Programming Tutorial for iOS: An Introduction

Editor’s observe: This week, we work with Ziad Tamim once more to provide you an introduction of iCloud programming. You’ll learn to save and retrieve knowledge from iCloud.On...

Read more

Easy multipart file upload for Swift

by learningcode_x1mckf
January 18, 2023
0
Easy multipart file upload for Swift

I imagine that you've got already heard in regards to the well-known multipart-data add method that everybody likes to add recordsdata and submit type knowledge, but when not,...

Read more
Next Post
Java Developer at Sabenza IT

Software Developer (C#, JavaScript) at Michael Page South Africa Limited

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Related News

5 Things to Know While Hiring a Java Developer

5 Things to Know While Hiring a Java Developer

October 3, 2022
How to Get the First Match From a Python List or Iterable – Real Python

How to Get the First Match From a Python List or Iterable – Real Python

October 26, 2022
JavaScript Slows Progress, Should be Retired, Argues JSON Creator

TIOBE Calculates C++, C, and Python Rose the Most in Popularity in 2022

January 8, 2023

Browse by Category

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

RECENT POSTS

  • JobRunr, the Java Scheduler Library, Released Version 6.0 – InfoQ.com
  • An Introduction to Lodash and Its Benefits for JavaScript Developers – MUO – MakeUseOf
  • "Used properly, Python is not slower than C++" – eFinancialCareers (US)

CATEGORIES

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

© 2022 Copyright Learning Code

No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#

© 2022 Copyright Learning Code

Are you sure want to unlock this post?
Unlock left : 0
Are you sure want to cancel subscription?