GithubHelp home page GithubHelp logo

Comments (26)

HaakonL avatar HaakonL commented on August 26, 2024 1

Thanks, that solved the issue for me :)

from factory.

HaakonL avatar HaakonL commented on August 26, 2024 1

I wonder if it is now un-solved again, with the update to Factory 2?

App is the main project, and it holds a reference to the Bootstrap framework. It also references Core to know about the Protocols. But it does not reference Services or Persistence.

Bootstrap references Core where the Protocols are, and Services/Persistence where the concrete implementations are. Bootstrap ties the protocols and implementations together.

Services and Persistence references Core to know about the protocols.

Skjermbilde 2023-03-08 kl  13 56 41

from factory.

HaakonL avatar HaakonL commented on August 26, 2024 1

I will give this a shot in the linked example project, but I fear that it will lead to circular dependencies. The requirement is not self-imposed, though, but needed to completely separate the frontend from the backend, and a crucial part of the clean architecture philosophy.

from factory.

hmlongco avatar hmlongco commented on August 26, 2024 1

@drekka I'll see if I can clear some of that up in the documentation.

That said, I've been promoting the optional approach primarily because doing fatalError and going boom is kind of what Factory was trying to avoid in the first place. ;)

from factory.

hmlongco avatar hmlongco commented on August 26, 2024 1

Closing this as I think most of the questions were answered and since the module documentation was improved.

Thanks all.

from factory.

HaakonL avatar HaakonL commented on August 26, 2024

https://github.com/HaakonL/Speedfriends

In there, I have a multi project setup with xcworkspace, clean architecture and resolver. Tried porting it to Factory without luck.

from factory.

hmlongco avatar hmlongco commented on August 26, 2024

See if this helps...
https://betterprogramming.pub/factory-multiple-module-registration-f9d19721a31d?sk=a03d78484d8c351762306ff00a8be67c

from factory.

HaakonL avatar HaakonL commented on August 26, 2024

Example project: https://github.com/HaakonL/Speedfriends/tree/factory2

from factory.

hmlongco avatar hmlongco commented on August 26, 2024

2.0 changed things up, true, but anything that worked and was visible in 1.0 should be equally visible in 2.0, provided it's publicly marked as such.

See: https://hmlongco.github.io/Factory/documentation/factory/modules

from factory.

HaakonL avatar HaakonL commented on August 26, 2024

Hmm, no, this wont work (for me), it will break the principle that the Core module, referred to as ModuleP in your documentation, should have no internal or external dependencies.

from factory.

hmlongco avatar hmlongco commented on August 26, 2024

So having Core import Factory would break a self-imposed rule? From my perspective, the DI system lives outside of the normal chain of command.

Only other option then, as far as I can see, is then to create a Dependencies module that can do the same thing. It can see core, and other modules need to import Core and Dependencies to get models AND access to APIs.

from factory.

ninokierulf avatar ninokierulf commented on August 26, 2024

See if this helps...
https://betterprogramming.pub/factory-multiple-module-registration-f9d19721a31d?sk=a03d78484d8c351762306ff00a8be67c

After reading the article, can I assume that multiple modules requiring a Factory<T> will need to depend on a lower module that defines the optional pattern var service: Factory<ServiceContract?> { self { nil } }? Because with Inversion of Control, there should be no concrete implementation at the level of requesting of desired service. Any injected object will then always have the guard check.

Example Module Structure:

App - where concrete services should be registered
^^^
Feature Modules - objects requiring a non-concrete service to be injected
^^^^^^^^^^^^^
Core - ??? place where service Factory are defined (but nil at this point)

from factory.

drekka avatar drekka commented on August 26, 2024

Just read the article and realised I've done exactly that setup in the project I'm working on.

In mine, I have a "domain" module which contains all the models and service protocols, a "UI" module which has all the UI views and view models, and a "services" module which implements "domain"'s service protocols. The only place which has access to both the views from "UI" and the service implementations from "services" is the app itself. So if a view model in "UI" needs a service, it can only see the protocol, not the implementation and visa versa, the services cannot see the views.

Factory 2 made this easy to deal with. However instead of registering nil optionals in "domain", I've registered fatalError(...) because I don't want optional services and also because I want the app to fall had and fast if I've stuffed up.

The only thing I haven't done is use the auto-registering stuff because I hadn't seen it. So I'll be updating my code as it's clearly superior to manually managing things and will remove a couple of @LazyInjected(...) properties I had in my main app class.

from factory.

drekka avatar drekka commented on August 26, 2024

That said, I've been promoting the optional approach primarily because doing fatalError and going boom is kind of what Factory was trying to avoid in the first place. ;)

Yeah it's one of those programming problems I think comes down to context. ie. does your code swallow an error (or developer mistake) and keep going if it can, or does it assert more aggressively, displaying an error or shutting down. I tend to lean towards the more aggressive approach only because I've spent a lot of time in the past debugging strange issues because some developer decided to just quietly swallow some error in the code, or because they intended to handle it and just never got back to that section of code.

But that naturally conflicts with fatalError which is something I also try to avoid as it's a terrible user experience. But which I also do use when I'm dealing with something that is absolutely a developer mistake (not a data issue) that should never get out of development. So again, I guess. Context.

from factory.

hmlongco avatar hmlongco commented on August 26, 2024

@drekka What if you could do something like...

extension Container {
    var serviceThatNeedsRegistration: Factory<MyService?> { promised() }
}

Based on something like...

extension ManagedContainer {
    public func promised<T>(key: String = #function) -> Factory<T?>  {
        Factory<T?>(self, key: key) {
            #if DEBUG
            fatalError("\(T.self) was not registerd")
            #else
            nil
            #endif
        }
    }
}

So app crashes and fails fast in debug mode, but doesn't when released?

Also cleans up and explains the rather strange self { nil } thing....

from factory.

hmlongco avatar hmlongco commented on August 26, 2024

Actually I've been playing with that and like it more and more.

extension Container {
    var promisedService: Factory<MyService?> { promised() }
}

Will probably make it into 2.1...

from factory.

drekka avatar drekka commented on August 26, 2024

Yeah, it's look really nice. Particularly the promised() which is clearly saying "Hey - this will be filled in later."

from factory.

drekka avatar drekka commented on August 26, 2024

The only issue I can think of is that you may need to have some sort of switch so that you can run with nils in debug during testing (unit and UI) because you will still need a way to test out nil responses. Technically never occur because the fatals will make developers fill in the blanks, but because the factory is still returning an optional, the rest of the code has to handle it and therefore "technically" what happens when a nil is return should tested.

from factory.

hmlongco avatar hmlongco commented on August 26, 2024

Thought of that.

FactoryContext.promiseTriggersError = false

from factory.

hmlongco avatar hmlongco commented on August 26, 2024

Keep in mind WIP, but....

from factory.

hmlongco avatar hmlongco commented on August 26, 2024

@drekka Promised is in the 2.1 develop branch. Also note that if you pull the 2.1 branch I updated the DocC documentation for multiple modules fairly heavily.

If you could glance at it and see if it clears up some of the confusion I'd appreciate it.

from factory.

drekka avatar drekka commented on August 26, 2024

Will do.

from factory.

hmlongco avatar hmlongco commented on August 26, 2024

@drekka Did you leave a note regarding archiving issues? Just archived test project w/o problems.

from factory.

drekka avatar drekka commented on August 26, 2024

Yeah, sorry. Was my bad. Didn't clear the project space before building.

from factory.

ninokierulf avatar ninokierulf commented on August 26, 2024

See if this helps...
https://betterprogramming.pub/factory-multiple-module-registration-f9d19721a31d?sk=a03d78484d8c351762306ff00a8be67c

After reading the article, can I assume that multiple modules requiring a Factory<T> will need to depend on a lower module that defines the optional pattern var service: Factory<ServiceContract?> { self { nil } }? Because with Inversion of Control, there should be no concrete implementation at the level of requesting of desired service. Any injected object will then always have the guard check.

Example Module Structure:

App - where concrete services should be registered ^^^ Feature Modules - objects requiring a non-concrete service to be injected ^^^^^^^^^^^^^ Core - ??? place where service Factory are defined (but nil at this point)

I realized I could use the Null Object pattern to avoid littering codebase with optional.

public protocol EmailAuthServiceProtocol {
    func authenticate(email: EmailAddress, password: EmailPassword) async throws -> User
}

public struct NullEmailAuthService: EmailAuthServiceProtocol {
    public init() {}

    public func authenticate(email: EmailAddress, password: EmailPassword) async throws -> User {
        throw EmailAuthError.nullObject
    }
}

public enum EmailAuthError: Error {
    case nullObject
}

lower level module can define

extension Container {
    var emailPasswordAuth: Factory<EmailAuthServiceProtocol> {
        self { NullEmailAuthService() }
    }
}

and Root composition level can override by

Container.shared.emailPasswordAuth.register { RealEmailAuthService() }

from factory.

hmlongco avatar hmlongco commented on August 26, 2024

@ninokierulf Bonus points for NullObject. That's a DI staple and definitely a solution that probably should be mentioned in the docs.

That said, I can see instances where implementing it might be difficult or lead to unexpected application behavior. If, for example, your protocol has var email: String { get } then you're going to have to return something. You can't throw as you could with the async function.

There are always tradeoffs....

from factory.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.