Created
October 8, 2019 10:21
-
-
Save siemensikkema/fada3547536e64f863275c8b957fde2a to your computer and use it in GitHub Desktop.
Example of how we can use repositories in Vapor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import FluentMySQL | |
import Vapor | |
// MARK: Models | |
final class User: MySQLModel { | |
var id: Int? | |
var active: Bool | |
var courses: Siblings<User, Course, UserCoursePivot> { | |
return siblings() | |
} | |
} | |
final class UserCoursePivot: MySQLPivot { | |
typealias Left = User | |
typealias Right = Course | |
static var leftIDKey: WritableKeyPath<UserCoursePivot, Int> = \.userId | |
static var rightIDKey: WritableKeyPath<UserCoursePivot, Int> = \.courseId | |
var userId: Int | |
var courseId: Int | |
var id: Int? | |
} | |
final class CourseLecturePivot: MySQLPivot { | |
typealias Left = Course | |
typealias Right = Lecture | |
static var leftIDKey: WritableKeyPath<CourseLecturePivot, Int> = \.courseId | |
static var rightIDKey: WritableKeyPath<CourseLecturePivot, Int> = \.lectureId | |
var courseId: Int | |
var lectureId: Int | |
var id: Int? | |
} | |
final class Course: MySQLModel { | |
var id: Int? | |
var lectures: Siblings<Course, Lecture, CourseLecturePivot> { | |
return siblings() | |
} | |
} | |
final class Lecture: MySQLModel { | |
var id: Int? | |
} | |
// MARK: "Controllers" | |
struct APIUserController { | |
// just 1 repository per controller, easy to mock and test | |
let repository: APIUserRepository | |
// ... actual endpoints using the repository come here | |
} | |
struct APICourseController { | |
let repository: APICourseRepository | |
// ... actual endpoints using the repository come here | |
} | |
// MARK: Repository Protocols | |
protocol APIUserRepository: UserCoursesRepository { | |
func users() -> Future<[User]> | |
func activeUsers() -> Future<[User]> | |
} | |
protocol APICourseRepository: UserCoursesRepository { | |
func deleteCourseWithLectures(_ course: Course) -> Future<Void> | |
} | |
// An example of common functionality for multiple repositories. Combined using protocol composition. | |
protocol UserCoursesRepository { | |
func courses(for user: User) -> Future<[Course]> | |
} | |
// MARK: Concrete Repository | |
// the actual database repository consist of mostly generic building blocks. | |
struct MySQLRepository { | |
let pool: DatabaseConnectionPool<ConfiguredDatabase<MySQLDatabase>> | |
func all<M: MySQLModel>( | |
on connection: MySQLConnection, | |
modifyQuery: (QueryBuilder<MySQLDatabase, M>) -> Void = { _ in } | |
) -> Future<[M]> { | |
let query = M.query(on: connection) | |
modifyQuery(query) | |
return query.all() | |
} | |
func first<M: MySQLModel>( | |
on connection: MySQLConnection | |
) -> Future<M?> { | |
return M.query(on: connection).first() | |
} | |
} | |
// MARK: Repository conformances | |
// the protocol implementations for the repository use the building blocks to provide the specific | |
// functionality that the controllers need. | |
extension MySQLRepository: APIUserRepository { | |
func users() -> Future<[User]> { | |
return pool.withConnection { self.all(on: $0) } | |
} | |
func activeUsers() -> Future<[User]> { | |
return pool.withConnection { | |
self.all(on: $0) { | |
$0.filter(\.active == true) | |
} | |
} | |
} | |
} | |
extension MySQLRepository: APICourseRepository { | |
// this is an example of a "complex" action where we use a transaction to make sure all can be rolled back | |
func deleteCourseWithLectures(_ course: Course) -> Future<Void> { | |
pool.withConnection { | |
$0.transaction(on: .mysql) { transaction in | |
try course | |
.lectures | |
.query(on: transaction) | |
.delete() | |
.flatMap { | |
course.delete(on: transaction) | |
} | |
} | |
} | |
} | |
} | |
extension MySQLRepository: UserCoursesRepository { | |
func courses(for user: User) -> Future<[Course]> { | |
return pool.withConnection { | |
try user.courses.query(on: $0).all() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment