Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This overlooks the actual problem. In Rails, you have a db schema, models are tightly coupled to the db schema, then the community push is to put logic into these models.

Having two models pointing at the same table is disregarded as bad idea.

Then, validation for form data is coupled directly to the db schema, thanks to models, not separated by scenario.

Then, the json rendering part is coupled to the model, because it's "convention over configuration" and the community pushes for DRY everywhere, without concern for the cost. So now, the json api is surfacing any change to the underlying STORAGE mechanism of the data, instead of being stable.

Now, add a react + Typescript interface that so popular nowdays on top of it and the result is that an update to the DB schema causes changes in the model, in the controller, in the json view, potentially breaking typings in the frontend.

This is the most common situation in Rails applications.

Now, let's take this situation 2 years in the future:

The json produced by this app is massive, there are many more new endpoints. Each endpoint has json rendering, but DRY is a hard requirement, without evaluating the risk, so every json rendering for a given model is reused in other json rendering objects.

Now one change of the underlying storage ripples through many, many API endpoints, potentially affecting hundreds of files.



I think you misunderstood the thrust of my argument.

A Rails model that talks directly to an RDBMS table, does indeed couple your business logic directly to your data storage representation. I'm not arguing with that.

There are two ways to fix this, though. And most people only consider one of them.

The "software-oriented" way to fix the coupling — and the one that software developers usually reach for — is to embrace domain-driven Hexagonal architecture on the inward edge: to create a domain layer within the business layer that defines its own non-DB-synchronized domain objects; and then to write an explicit DB gateway that can be Commanded and Queried with those domain objects, where that layer translates those domain objects into relational row-tuples.

(Note that this is separate in concept from embracing Hexagonal architecture on the outward edge. Gateway abstractions + internal-domain/external-view model isolation+translation are well-suited concepts to encapsulating the different rates-of-change needs of your system vs. the external systems it interacts with. That's what the Hexagonal-architecture concept was invented for, and why MVC separates Controllers from Views.)

The "database-oriented" way to fix the coupling, is to continue to model your business domain within the DB, but to model it in a faceted, denormalized way, such that each query-schema gets its own view, and each command-schema gets its own writable view driven by a trigger + stored procedure, and the tables where data actually lives (and what columns/types/indices they have) are an implementation detail of how those views were written. Just like in OOP, you hide the data, encapsulating it behind an API (the views.)

When your Rails models bind to these "schemas", each Rails model then inherently represents a Query-result or a Command-changeset — without having to complexify your business layer. These objects can just be your domain objects. You're getting your domain objects directly from the database, and sending them directly into the database, because the database itself contains the translation layer mapping the domain into the storage representation. Just as with the first approach, you're free to change these mappings at any time — you just add some DB schema migrations which add/update/remove the views backing the models, and re-point your models at the new views. (If you create these views using unique-per-version names, then different versions of your app can even live in peace together, with all their versioned bits-and-pieces of translation layer co-existing and being shared between where relevant.)


Oh, yeah I definitely misunderstood your point.

This makes sense and it's an idea that makes me excited, I wish there was a community drive toward that.

I do think that most Rails developer will dismiss it because some reason like "It's weird to have a Model backed by a view" or something along those lines. I wish the Rails community pushed (way) harder on dramatically increasing SQL knowledge, because it's so important to the whole framework.

Instead most of that SQL knowledge is hidden behind active record, making it incredibly hard to acquire.

There is one thing that this approach doesn't solve, and that is the inherent flaw of activerecord: having Save on the business logic entity makes it so that returning such object back from a business-logic method is dangerous, since now any other code could potentially write to the DB (and this is what usually happens due to lack of software design principles).

I suspect you are going to suggest something along the lines of "set readonly some records and command records are only used in specific spots", which could make sense!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: