Trying to define models (as Taxi defines them) statically is often a source of hidden technical debt as models change over time and different teams in different parts of an org can't agree on what a model should contain.
I like that the creator of Taxi is already aware of the anti-pattern lurking behind common domain modeling but doesn't take those lessons learned far enough.
Case in point: the field names in models themselves should be the actual Types (again, in the nomenclature of Taxi)
`firstName: FirstName` is redundant. It is equivalent to an alias.
Instead a model should just be a open set of Types (I would probably call them attributes).
That's what Clojure Spec gets right from the get go and leads to much more flexible and open systems.
Given a model Person with something like this: firstName: FirstName middleName: MiddleName lastName: LastName ssn: SSN
If I only have a FirstName and LastName and don't know the ssn yet, I can't pass these around without having to define an extra model `PersonButWithoutSSN`. I can try to mark ssn as optional, but aren't all these fields optional? And in what combination?
Lastly I would advise against enforcing business logic in models, which is what the conditional blocks are effectively doing. Even if just used for data transformation: You'll run the danger of embedding a full scripting language (and you are pretty far already in that direction)
Stick to data coercion on the Type level. Any other transformation should be out of scope or defined separately to maximize interop.