Lots of ways. Defining appropriate views is one of the key ones, but not the only one.
> How do I apply type safety principles?
That's too vague of a question to present a concrete answer to.
> It feels like this ends up with building some sort of ORM.
Anytime you are using a database from an external language, you are going to have some code to translate back-and-forth between database objects and operations and application language objects and operations; you might call it an ORM, but if you are using SQL more robustly, it may not look much like most things that go under the ORM label, and some popular ORMs are fairly opinionated in ways which fight effective use of SQL (ActiveRecord in particular at least used to be; I haven't done much with it in quite a while and am not sure if that's still the case.)
This API lends itself to TDD, and yes, you must test your database components the same as any other code. Preferably using database code. For PostgreSQL I really liked PGTap. Include this testing in CI and other unit test executions.
My sniff-test for a well-done schema is that developing application(s) around it should be clean.
The downside is that you need to express the command language, so you lose the ad-hoc querying capability that SQL gives you. But something more structured and more closely corresponding to the domain is generally want I want anyway.
If the needs have changed, the API should be upgraded. It seems like ad-hoc querying is mainly to get around shortcomings in the API, right?
...until your broken data starts biting you and your customers in places that don't typically see sunlight.
There are no places in any code base at all where it doesn't pay to be rigorous about DRY.
>It turns out, your model and validations don't actually prevent broken data.
A) DRY isn't about preventing broken data.
B) Validations are for preventing broken data. That's what they do.
Remote facade sounds like a lot of boilerplate to me.
I'll offer some of the reasoning that got me to building remote facades. The database needed to sanitize its own inputs; we could not trust application developers with that. There would be multiple client-languages (Ruby, Python, Java, for starters) and I wanted to keep domain adapters for those languages simple as possible. The database would be changing regularly, and many changes could be simply absorbed in the remote facade, so applications depending on the remote facade via their client-language libraries would be somewhat insulated.
Now, it wasn't a perfect regime, and nothing is. But it paid the bills for years and my schemas are still in play at that place.
For a large org, Slick also adds to the already significant Scala learning curve for Java devs. Scala itself is good and pretty, but when the first task you give someone new is to write a CRUD-like rest service using slick on one end, and spray on the other, they don't get to spend any time learning actual scala, and instead domain specific languages that are hard to reason about without serious Scala experience. No, this is not speculation: We are going through this problem at work right now, with dozens of developers learning Scala, and only a few of us that really started with significant experience.
If I have to use slick, I use the few direct SQL bits. From my perspective, is the one saving grace of the library.
What library would you use instead of slick?
For example, how do I apply DRY and use SQL ? How do I apply type safety principles? How do I avoid syntax errors?
It feels like this ends up with building some sort of ORM.
Anyways, I'd love to hear experiences on interfacing SQL with "good" safety principles in larger-scale projects