DEV Community

Discussion on: Does Phoenix make database CRUD too easy?

Collapse
 
dmfay profile image
Dian Fay

I'm neither a Ruby nor an Erlang/Elixir developer so I can't speak to most of this, but I am going to go ahead and challenge the idea that 'business logic is happier without knowing it works with a database' :)

There are arguments for ensuring that the logic is portable without regard for a particular flavor of (relational, since if you're going NoSQL you're already locked in) database, most commonly that you're building an enterprise product which will be deployed in environments that already run aSQL with admins who don't want to have to learn and manage bSQL. But it's not a foregone conclusion.

Databases and SQL are better at many things than higher-level general purpose programming languages. When user input isn't required, it makes no sense to pull information out of a database, operate on it in application code, and put it back when SQL could accomplish the same goals in a fraction of the time. Database constraints too are the only way to truly enforce data validity and integrity, so you're already performing some business logic functions in the database. This stuff never really happens in a vacuum, and coming to grips with that is a lot more sustainable than trying to pretend otherwise.

Collapse
 
michaelherold profile image
Michael Herold

I've thought about your comment for a few days now. Here are my thoughts on the topic, not in the order in which you presented them since I think my thoughts will be more coherent that way. Hopefully, that's okay! :)

When user input isn't required, it makes no sense to pull information out of a database, operate on it in application code, and put it back when SQL could accomplish the same goals in a fraction of the time.
[...]
admins who don't want to have to learn and manage bSQL.

I'm curious about this. It sounds to me like you're suggesting you use things like triggers or PL/pgSQL (in the Postgres world, of course!) to directly update data within the database? Is that something you do? How does it work out? Have you run into any maintenance issues there?

I've often thought that triggers would a nice thing to use, but splitting your logic between the database and the application feels like it would lead to a maintenance headache in the form of increased cognitive load. When there is a bug in the application, you then have to look in multiple places for the source of the bug. In addition, there's more "surface area" for onboarding new developers onto the application.

Maybe I misunderstood and you're not suggesting that. If I did misunderstand, I'd love to hear what else you meant!

I am going to go ahead and challenge the idea that 'business logic is happier without knowing it works with a database' :)
[...]
Database constraints too are the only way to truly enforce data validity and integrity, so you're already performing some business logic functions in the database.

Between these two statements, I think that you and I mean two different things when we say "business logic". To me, business logic consists of all the things that can't (easily, at least ... I know you can do some magic things in procedural RDBMS languages) express in the confines of SQL and/or relational algebra.

Business logic is why we have applications instead of spreadsheets.

Data integrity is a separate, related set of logical changes to the data that is input by the humans and other systems into your application. Constraints help protect you from errors. They help you clean your data as it changes over time (for instance, foreign key constraints with ON DELETE CASCADE).

Data integrity is there to help you reduce the cognitive load in your business logic and, hopefully, make it so you can write your business logic how a human thinks about it, not how you have to tell a computer to do it.

Does that make sense? Thank you for the thoughtful comment!

Collapse
 
dmfay profile image
Dian Fay

I make extensive use of views, triggers, functions, and stored SQL scripts (which last my data access framework treats ~identically to functions; to bring this back to Elixir momentarily, Moebius is built toward similar goals by Massive's original developer). Lately I've applied tobyhede's Postgres event sourcing model in a couple of cases, and of course trying to do that outside SQL would be impractical at absolute best.

I definitely agree that splitting business logic can lead to headaches -- which is why I try to put as much of it as possible in the database and write fairly minimal higher-level APIs on top! All you need for that is a simple web framework, while migration tools like sqitch make it practical to iterate on the database the same way you do on the higher-level code. Of course this all does require some facility with SQL, or the time and willingness to learn.

Testing is also a concern. pgTAP exists but so far ensuring API routes exercise the database fully and maintaining good test coverage for those has served me well enough.

Data integrity is there to help you reduce the cognitive load in your business logic and, hopefully, make it so you can write your business logic how a human thinks about it, not how you have to tell a computer to do it.

If you ride that train of thought a little further, the boundary will start to get get really, really blurry. It's a false dichotomy: business logic as you presently conceive of it is a set of rules which describe things happening to information. So are integrity constraints: even your example of CASCADE on a foreign key is a rule which declares that when a tuple is deleted, tuples in another table with an attribute referencing it must also be deleted. That's low-level business logic!

To pontificate a little: web developers often have a weakness for synecdoche, taking 'application' to mean strictly a subset of data processing and user-facing components written in higher-level languages. When you think instead of a complete system assembled to achieve a purpose, the database is more than simply a storage device: it's an organizing principle. The database describes how information is structured and what can or can't be stored. Data storage considerations shape the architecture of higher-level components (how many XControllers have you run into where X is a table?). It's a mistake to think of the database as a wholly separate concern. If something would be faster or simpler as a trigger and you don't have a pressing reason to minimize database usage, make it a trigger! That's why the database developers gave you the option!