Skip to content

Commit

Permalink
Merge pull request #18 from rokon-uddin/repository_segregation
Browse files Browse the repository at this point in the history
chore: refactor repository to separate interfaces that the repository…
  • Loading branch information
Mohammed Rokon Uddin authored Feb 14, 2024
2 parents b6a161d + b9aa4c0 commit 5c54c9e
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,60 @@

import Foundation

public protocol Repository {
public protocol PrepareRepository {
func prepare() async throws
}

public protocol CreateRepository {
associatedtype CreateInput = Never
associatedtype CreateOutput = Never
associatedtype RepositoryType

func create(input: CreateInput) async throws -> CreateOutput
static var live: RepositoryType { get }
static var stubbed: RepositoryType { get }
}

public protocol ReadRepository {
associatedtype ReadInput = Never
associatedtype ReadOutput = Never
associatedtype RepositoryType

func read(input: ReadInput) async throws -> ReadOutput
static var live: RepositoryType { get }
static var stubbed: RepositoryType { get }
}

public protocol ReadAllRepository {
associatedtype ReadInput = Never
associatedtype ReadOutput = Never
associatedtype RepositoryType

func readAll(input: ReadInput) async throws -> [ReadOutput]
static var live: RepositoryType { get }
static var stubbed: RepositoryType { get }
}

public protocol UpdateRepository {
associatedtype UpdateInput = Never
associatedtype UpdateOutput = Never
associatedtype RepositoryType

func update(input: UpdateInput) async throws -> UpdateOutput
static var live: RepositoryType { get }
static var stubbed: RepositoryType { get }
}

public protocol DeleteRepository {
associatedtype DeleteInput = Never
associatedtype DeleteOutput = Never
associatedtype RepositoryType

func prepare() async throws
func create(input: CreateInput) async throws -> CreateOutput
func read(input: ReadInput) async throws -> ReadOutput
func update(input: UpdateInput) async throws -> UpdateOutput
func delete(input: DeleteInput) async throws -> DeleteOutput

static var live: RepositoryType { get }
static var stubbed: RepositoryType { get }
}

public typealias RemoteProductRepository = CreateRepository & ReadRepository

public typealias PersistentProductRepository = CreateRepository & ReadRepository
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import Foundation

public final class PrepareCoreDataUseCase: UseCase {
var prepare: () async throws -> Void
public init<R: Repository>(repository: R) {
self.prepare =
repository.prepare
public init<R: PrepareRepository>(repository: R) {
self.prepare = repository.prepare
}

public func execute(input: Void) async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ public final class ProductUseCase: UseCase {

var getProduct: (_ input: Int) async throws -> Product?

public init<R: Repository>(repository: R)
public init<R: RemoteProductRepository>(repository: R)
where R.ReadInput == Input, R.ReadOutput == Output {
self.getProduct =
repository.read(input:)
self.getProduct = repository.read(input:)
}

@Sendable public func execute(input: Int) async throws -> Product? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@
// Copyright © {% now 'utc', '%Y' %} {{cookiecutter.company_name}}. All rights reserved.
//

import Combine
import Foundation

public final class SaveProductUseCase: UseCase {
var saveProduct: (_ input: Product) async throws -> Void

public init<R: Repository>(repository: R)
public init<R: PersistentProductRepository>(repository: R)
where R.CreateInput == Input, R.CreateOutput == Output {
self.saveProduct =
repository.create(input:)
self.saveProduct = repository.create(input:)
}

@Sendable public func execute(input: Product) async throws {
Expand Down
21 changes: 12 additions & 9 deletions {{cookiecutter.app_name}}/Features/Sources/App/AppClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,18 @@ extension DependencyValues {

extension AppClient: DependencyKey {
public static var liveValue = AppClient(
PrepareCoreDataUseCase(repository: PersistentRepository.live),
productUseCase: ProductUseCase(repository: NetworkRepository.live),
saveProduct: SaveProductUseCase(repository: PersistentRepository.live))
PrepareCoreDataUseCase(repository: PreparePersistentRepository.live),
productUseCase: ProductUseCase(repository: RemoteProductRepository.live),
saveProduct: SaveProductUseCase(
repository: PersistentProductRepository.live))
public static var testValue = AppClient(
PrepareCoreDataUseCase(repository: PersistentRepository.live),
productUseCase: ProductUseCase(repository: NetworkRepository.stubbed),
saveProduct: SaveProductUseCase(repository: PersistentRepository.live))
PrepareCoreDataUseCase(repository: PreparePersistentRepository.live),
productUseCase: ProductUseCase(repository: RemoteProductRepository.stubbed),
saveProduct: SaveProductUseCase(
repository: PersistentProductRepository.live))
public static var previewValue = AppClient(
PrepareCoreDataUseCase(repository: PersistentRepository.live),
productUseCase: ProductUseCase(repository: PersistentRepository.stubbed),
saveProduct: SaveProductUseCase(repository: PersistentRepository.live))
PrepareCoreDataUseCase(repository: PreparePersistentRepository.live),
productUseCase: ProductUseCase(repository: RemoteProductRepository.stubbed),
saveProduct: SaveProductUseCase(
repository: PersistentProductRepository.live))
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Domain
public struct AppFeature: FeatureReducer {

@Dependency(\.appClient) var appClient

public init() {}

public struct State: Equatable, Hashable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,15 @@

import Domain

public struct NetworkRepository: Repository {
public struct RemoteProductRepository: Domain.RemoteProductRepository {

private let network: AppNetworking

private init(network: AppNetworking) {
self.network = network
}

public func prepare() {
fatalError("Unimplemented")
}

public func create(input: Int) async throws -> Product? {
public func create(input: Product) async throws {
fatalError("Unimplemented")
}

Expand All @@ -29,19 +25,11 @@ public struct NetworkRepository: Repository {
.product(id: input),
type: Product.self)
}

public func update(input: Int) async throws -> Product? {
fatalError("Unimplemented")
}

public func delete(input: Int) async throws -> Product? {
fatalError("Unimplemented")
}
}

extension NetworkRepository {
public static var live = NetworkRepository(
extension RemoteProductRepository {
public static var live = RemoteProductRepository(
network: AppNetworking.defaultNetworking())
public static var stubbed = NetworkRepository(
public static var stubbed = RemoteProductRepository(
network: AppNetworking.stubbingNetworking())
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,14 @@

import Domain

public struct PersistentRepository: Repository {
public struct PersistentProductRepository: Domain.PersistentProductRepository {

private let persistenceController: PersistenceController

private init(persistenceController: PersistenceController) {
self.persistenceController = persistenceController
}

public func prepare() async {
do {
try await persistenceController.prepare()
} catch {
fatalError(error.localizedDescription)
}
}

public func create(input: Domain.Product) async throws {
await persistenceController.update(
entityType: Product.self, createIfNil: true
Expand All @@ -32,6 +24,7 @@ public struct PersistentRepository: Repository {
$0?.title = input.title
$0?.price = input.price
$0?.image = input.image
//FIXME: may be rating is a reserved keyword.
// $0?.rating = product.rating.toManagedObject(in: persistenceController.container.newBackgroundContext())
$0?.category = input.category
$0?.productDescription = input.description
Expand All @@ -42,18 +35,11 @@ public struct PersistentRepository: Repository {
await persistenceController.fetchDomainObject(entityType: Product.self)
}

public func update(input: Int) async throws -> Product? {
fatalError("Unimplemented")
}

public func delete(input: Int) async throws -> Product? {
fatalError("Unimplemented")
}
}

extension PersistentRepository {
public static var live = PersistentRepository(
extension PersistentProductRepository {
public static var live = PersistentProductRepository(
persistenceController: PersistenceController.shared)
public static var stubbed = PersistentRepository(
public static var stubbed = PersistentProductRepository(
persistenceController: PersistenceController.shared)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// PreparePersistentRepository.swift
// PersistentPlatform
//
// Created by {{ cookiecutter.creator }} on {% now 'utc', '%d/%m/%Y' %}.
// Copyright © {% now 'utc', '%Y' %} {{cookiecutter.company_name}}. All rights reserved.
//

import Domain
import Foundation

public struct PreparePersistentRepository: Domain.PrepareRepository {

private let persistenceController: PersistenceController

init(persistenceController: PersistenceController) {
self.persistenceController = persistenceController
}

public func prepare() async {
do {
try await persistenceController.prepare()
} catch {
fatalError(error.localizedDescription)
}
}
}

extension PreparePersistentRepository {
public static var live = PreparePersistentRepository(
persistenceController: PersistenceController.shared)
}

0 comments on commit 5c54c9e

Please sign in to comment.