Remix.run Logo
kybernetikos 4 days ago

I've become convinced that the real problem is probably impossible to get away from.

Ultimately we want a nice set of reusable UI components that can be used in many different situations. We also want a nice set of business logic components that don't have any kind of coupling with the way they get represented.

In order to make this work, we're going to need some code to connect the two, whether it's a 'controller' or a 'view model' or some other piece of code or architecture.

However we choose to achieve this task, it's going to feel ugly. It's necessarily the interface between two entirely different worlds, and it's going to have to delve into the specifics of the different sides. It's not going to be the nice clean reusable code that developers like to write. It's going to be ugly plumbing, coupled code that we are trying to sweep into one part of the codebase so that we can keep the rest of it beautiful.

feoren 4 days ago | parent | next [-]

These seemingly inescapable tradeoffs are almost always actually quite escapable if you look at it from a different perspective. You have to stop thinking about pages and button-clicks, and you have to stop using frameworks that try to do everything, box you in, and force you to architect your state-flow logic based on your visual hierarchy. This is the biggest problem with almost all UI frameworks: all your logic has to be partitioned along the lines that are set up by how your screen looks, or you're swimming upstream to prevent it. Instead, have your domain models declare how they work and interact using intermediate services, and consume those services to generate the UI as a consequence of those declarations. It's very hard, and I don't have all the answers yet, but I've tasted enough to know that it genuinely avoids this otherwise seemingly inescapable tradeoff. I'm not planning to ever build another UI (above some complexity) differently again.

Izkata 3 days ago | parent [-]

Speaking of a different perspective, you and GP are describing it with different viewpoints and I'm not sure you're fully aware it is different:

GP is describing the Model and View as two co-equal things with something in between that links then.

You're describing the Model more as a foundation that the View builds on top of (and this is also how I look at it).

I like the stacked view more because it more closely matches the flow of data, making it almost explicit that what's presented to the user is a projection/transformation of the stored data. Views can't really exist independent of that data, they rely on it, while models can exist independent of the view.

jmkni 4 days ago | parent | prev | next [-]

Yeah exactly, you can come up with all sorts of abstraction layers but your ugly code has to go somewhere eventually

vbezhenar 4 days ago | parent | prev [-]

Sounds like very simple task to solve.

You have Table component.

You have TableData interface. Table component asks TableData interface for data to show.

You have TableCallback interface. When user interacts with Table, like click or whatever actions are implemented, TableCallback methods are called.

When you want to use Table with your data, you implement TableData and TableCallback for your data. That's about it.

I've seen this approach implemented in most UI frameworks. You might rename TableData to TableModel or whatever, but essentially that's it.

kybernetikos 4 days ago | parent [-]

I'm not saying it's not simple to write, I'm saying it's ugly and contingent and you can't really avoid that. It's exactly this reason that has led to a proliferation of MV* patterns, including the one you describe.

But to try to explain myself more clearly - in the architecture you describe, who is that it is implementing TableData and TableCallback? Is it your beautiful clean business logic classes that have no coupling to their representation - in which case that is weirdly coupled in an ugly way, or is it some other class that acts as the bridge between the two worlds, in which case, that's where your ugly code is living.

ndriscoll 4 days ago | parent [-]

Ideally a bridge (e.g. a typeclass) with nice language support like Scala has. I'm not seeing what's so ugly. In math, you have an abstract interface for e.g. monoids (your interface like a Table). Then you have e.g. complex numbers as a set (your business model). The you can identify how C is a monoid via addition. And how it's also separately a monoid via multiplication. Same idea. There's nothing "ugly" about having to write down what you mean.