DeclarativeHub / TheBinderArchitecture

A declarative architecture based on bindings
MIT License
148 stars 6 forks source link

So all view logics are in the binder function now? #3

Open Albert-Gao opened 6 years ago

Albert-Gao commented 6 years ago

Thanks for the doc, seems an easier version of view model. Got one question.

Say I will send a request with some service, then depends on the result, I might do something different. Show a dialog if result A, go to another screen if result B, send request from another service if result C. All these logics, are now in the binder function? Seems a lot of code will be there...

At least to me, seems no other place to put them. Am I missing something?

npvisual commented 6 years ago

@Albert-Gao , if you look at the updated AbsurdGitter application, it seems that most of that logic resides in the session manager of the Service framework -- there's a good example there of going from the UnAuthenticatedSession (with the Login Service) to the AuthenticatedSession (with the User and Room Services), and vice-versa.

Seems to me, at least in this example, that the Binder functions merely create the bindings / glue between the view controllers and the services. But the logic around how the services interact together (the flow) is kept in the Services framework, which, theoretically should make it easier to unit test.

I think @srdanrasic 's key sentence here is :

Business logic layer data and events flow from the Service to the View Controller

Maybe that needs more detailed explanation / clarification to make it more obvious that the flow in the business logic is kept in the Services framework ?

srdanrasic commented 6 years ago

Thanks for the feedback @Albert-Gao.

Your example seems to represent the application logic so yes, it would be implemented in the binder. However that should be a few simple lines of code so I would not call that a lot of code - unless you are doing something extra.

I've been using this architecture on some rather complex projects and my binders are on average less 100 lines of code and never more that 200.

This might seem like a lot of code for a function, but the number of lines of code is a bad measurement of complexity. It does not matter how many bindings there are in the binder, what matters is how simple each of these bindings is.

A 200 line binder with 30 bindings that all do only assignments is a simple binder. On the other hand, a 20 line binder with one binding that combines data from multiple sources, transforms it, presents other view controllers and acts on their outcomes is a complex binder. If you find yourself implementing the latter you should reconsider if what you are doing is really only the application logic. Is some of it maybe a business logic that should live in Services or a view logic that should live in Views. If it really is the application logic then maybe it could be broken into smaller pieces using some other patterns.

Albert-Gao commented 6 years ago

Thanks guys! Really helps.

I think it's more clear now. So, the boundary between business logic and application logic is that the UI, anything manipulates the UI should reside in the application logic layer?

I think this pattern solves a problem in all mobile platforms where the view is finally a view. Otherwise, there are always some logic, be it binding or routing, you need to put in the view then the view becomes not that clean.

Furthermore, I suppose that things like viewDidLoad() is not needed, because all the preparation will be in the binder as well?

danielt1263 commented 2 years ago

I know this is old, but I want to chime in here... A function that creates a Signal/Observable/Publisher from basic types necessarily has side effects involved and should be called in the binder function. Likewise the bind/subscribe/sink closure that receives an event also contains side effects. However, the operators between the two are, or at lest should be, pure.

This is very much in the spirit of Gary Bernhardt's "Functional Core/Imperative Shell" idea. The outer edges of the signal chain are the imperative shell while the internal operators are the functional core. Break those internal operators into separate functions that take in one or more signals and return a signal and you will accomplish two things. One, reduce the amount of raw lines of code that is in the binder function and two, create chunks of bits of logic that can be independently tested and reused in other contexts... You can think of these functions as mini-view models without all the boilerplate required of a normal view model.

As a bonus, you will be able to test these logic functions without creating a bunch of mocks or injecting any dependencies.