0

I mostly write mobile apps so this is definitely coming from that perspective, but I think it applies for most UI development.

Suppose I have a UI with the following pattern:

App architecture diagram

I want to have some user interaction in one part of the UI, and another part of the UI that reacts to it. For instance, the user sets their profile image in the profile section, and it's shown at the top of the app everywhere immediately.

It seems that observable values are the solution to this, and those observables should be owned by the data layer and merely exposed or passed into the view layer.

The problem is that in this situation my data layer is big and complex. The data layer is a hierarchy, with different controllers in charge of different views, and with parent controllers that own child controllers. The question is, should the observable value be owned by the parent layer or one of the child layers?

It seems like both answers are wrong to me. If the parent owns it, then we'll just end up with a fat parent and the child controllers won't be doing much except passing data from the parent to their views. But if one child owns it, we could end up with a lot of entangling between siblings, and the parent doing a lot of work passing values between them.

I lean towards one of the children owning it, and the parent ignoring the specifics of the values their children are using and simply bridging between the children. In the profile example, a profile controller would hold the profile image observable value, and a parent controller would provide access to that observable value to the other controllers in the app. The other controllers would not know about the profile controller -- they would simply have access to that observable value and update their views in response to it.

2

2 Answers 2

2

A case against anemic model

If change monitoring is done via a fat controller, that effectively "observes" the whole model, the model becomes anemic. Such model can't use its own observables to react to change and relies on controller to handle all event propagation. This usually leads to disaster, as controller starts to handle business logic and as a result loses benefit of clear separation of concerns.

Solution

Make a decision based on your business logic, not on the layer structure. For example, consider an observable entity to be a resource that is owned by something (just like an opened file - an owner is responsible for closing it and a file pragmatically should only have one owner) and find the best suited owner. Similarly to open files, I usually rely of object lifetimes to make such decisions (other factors play a role, but lifetimes often provide a no-brainer solution, so it is useful to check them first).

Example

Lets consider a profile picture again.

A user profile has to know about picture, multiple views has to know about the picture, file storage or database layer (repository) store the picture.

While the picture is stored in a persistent repository, its lifetime is bound to an user account (i.e. the picture is not needed once user is deleted). Therefore, an observable picture should belong to user account entity instead of a global registry/repository. The answer is clear-cut and easy for this example.

Generalization

I use observables in a fat model to represent mutable business values. This is not always easy and often fat model has to establish internal subscriptions between entities, but the benefits are obvious - very high cohesion, very good separation of concerns.

-1

My advice would be that your instincts are wrong.

In general it is better to have one top level controller/view model holding the entire state of the application, or whatever section of the application is loaded if you are able to split its functionality up.

If you put business logic in child components, such as having a profile component load pictures from a database the orchestration of the various bit of logic becomes difficult.

Say for example i can change my picture on the profile component, but I also display my picture on some other component.

  • Do both load the image from storage? Do I reuse the profile component and have a flag to set it to read only?
  • If I change the picture on one do i trigger a reload, or send the image?
  • If I trigger a reload in the second component will that in turn trigger some other event?
  • Will i have an infinite loop?
  • When do i redraw the screen?
  • Is my application performant with these events flying all over the place?

If you have a single controller which the various events bubble up to, you can handle these scenarios there and these problems go away. You can keep your logic in Models and services and collect them a View Model

4
  • How does this avoid the "Massive View Controller" problem from having a single controller/viewmodel handle all the data that needs to go back and forth between all of the views in the app? Jul 30, 2023 at 21:32
  • Either way you have the same amount of code, split it amongst Models and Services, not View level components.
    – Ewan
    Jul 30, 2023 at 23:12
  • Distributed observables do not justify God-controller.
    – Basilevs
    Dec 30, 2023 at 10:34
  • "You can keep your logic in Models and services and collect them a View Model"
    – Ewan
    Dec 30, 2023 at 11:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.