> My personal opinion is that malformed data shouldn't be reaching domain code.
The mistake a lot of people make: Rails itself isn't domain code.
In hexagonal-architecture terms, Rails is a framework for building your "web gateway", not for building your domain logic. Your domain logic should live on its own, outside Rails, as plain-old code with a well-defined API (consisting of functions and domain types.)
Rails' job — like any other hexagonal-architecture gateway's job — is to translate in incoming requests into command or query objects to pass to the domain-logic API; and to translate the domain-logic API's generated events or query-result objects back out. The former is exactly an MVC controller; the latter is exactly an MVC view.
Think of it like building a (portable, multiplatform) game: your business-logic is like the game engine, assets, scripting, etc. Rails, meanwhile, is like the OS-specific glue code that makes your game run in a window and respond to mouse/keyboard input on Windows/macOS/Linux etc. This glue code takes events from each OS and brings them into your game; takes output from your game and feeds it back to OS drivers; and binds OS mechanisms like window controls to game features. You don't want any "game" logic to live inside your OS-specific glue code. You want your glue code to just be a "wrapper" that brings in your game as, essentially, a library, and then "wires it up" to the given OS.
In this view, both Strong Parameters and dry-schema are tools used "at parse time" — i.e. controller-execution time within the web gateway — to create the "strongly typed" "unique schema" for the route, that allows it to discard/reject data before putting it into the real strongly-typed objects: the business-domain objects used by your business-logic DDD context.
(And the reason that this gluing-together is done through an arbitrarily-programmable framework, rather than by just giving routes static strong types, is because the translation process itself — especially when compatibility with multiple legacy systems is involved — can be "Turing-hard", requiring a full programming language to specify what should be glued to what, and when it's valid to translate X to Y vs X to Z. Maybe some users are talking to v1.1 of an endpoint while some users are talking to v1.0, based on a per-user feature-flag or holdback-flag or whether the user is a paying customer or not.)