4 ms·
> the point of using a different layout/structure for storage vs the UI layer? By way of example, I'll point to the classic example of a New User form UI: that
by DaiPlusPlus 3y ago
> the point of using a different layout/structure for storage vs the UI layer?
By way of example, I'll point to the classic example of a New User form UI: that UI will need 2 password inputs (one for the password, the other for the "confirm password") - but your User object/schema/DB-table won't have two separate string password fields - it'll have a single binary/byte[] salted-password-hash field, and you certainly must never show that in the UI.
As for your (reasonable!) concerns about having to write-out repetitive types/models that all share similar (though hardly ever identical) data/fields/shape: I agree it's tedious, but that's what we have scaffolding-tools for (granted, I often end-up having to write my own scaffolding and templaying tools...), so for a simple CRUD application just design your database and have the scaffolding tools take care of the rest, including stubbing-out a functional UI (and this is why Ruby-on-Rails was huge when it first came out: it took-away all of the drudgery in CRUD web-applications - but then all the other frameworks/platforms improved their scaffolding-stories and RoR is certainly less attractive in comparison now.
-----
Oh, and of course, ChatGPT et al. is also incredibly effective at scaffolding entire solutions from scratch, e.g. from last month: https://bit.kevinslin.com/p/leveraging-gpt-4-to-automate-the https://bit.kevinslin.com/p/leveraging-gpt-4-to-automate-the
- hsn915 3y agoYou're citing an exception as if it was the rule. But even in this case, I don't have two representations of the same object. Rather I have two different object: Account (persisted) SignupRequest (not persisted) The SignupRequest is used to create the Account The signup form on the UI is about editing the SignupRequest object. This object will be sent from the UI code to the backend as-is. The backend code will use it as-is (ignoring the json encoding/decoding). There's a code path in the backend that takes SignupRequest and uses it to create a new Account.
- bitblender 3y agoIn this case SignupRequest is your contract representation, Account is your storage representation, and the "backend code path" is the transformer/napping layer. >I don't have two representations of the same object. Rather I have two different object Exactly! Your api contract and your storage are ALWAYS two different objects, because they serve two different concerns. Sometimes by coincidence they can share the same shape but there's no reason that they need to be coupled together and impossible to change independently, other than the fear of inconvenient "boilerplate" mapping logic. By doing this up front, and not even letting it enter your data model, you create a formal abstraction boundary; it's reserving the right to change two pieces of data independently. Also, mapping/transformation logic can often just be simple, pure, total functions; which are trivial test and maintain compared to anything that touches I/O.
- DaiPlusPlus 3y ago> Also, mapping/transformation logic can often just be simple, pure, total functions I find this is never the case - usually for the exact reason you inadvertently sold as some kind of "benefit": "it's reserving the right to change two pieces of data independently" - because when you need to map from, say `SignupRequest` to an `SavedAccount` object you'll encounter data-members required by `SavedAccount` which cannot be sourced from the `SignupRequest` object - for example, supposing the UX people come to us and say we need to split-up the registration form into 2 pages, such that most fields are still on page 1, but the password boxes are on page 2. Now you need to deal with how to safely persist data from page 1 for use in page 2 (so using hidden HTML inputs between pages won't work because that requires POST requests to work, but both pages should be able to be intiially-requested with a GET request.
- hsn915 3y ago> Exactly! Your api contract and your storage are ALWAYS two different objects Not really. I have request objects for everything. "Search" is a request object. List pagination is a request object. Every function exposed through the RPC API takes a request object and returns a response object. The response object is often just a collection of objects straight from "the database". A response to a paginated list request will contain a list of objects straight from the database, in addition to some metadata about the pagination (names: current page number, total page count). The cruicial part is there's no "transformation" of data as it goes out from the database into the UI. There's some aggregation and grouping (an outer object that contains multiple objects), but that's about it. Again though there's a subtlty: some transformations do occur, but they don't occur on the path from the storage to the UI. Instead, everytime I store a complex object, I also derive a "simple" version of the object and store it too. When you request a list of objects, you get the "simple" version, and the UI displays them in summary format. When the user clicks one of them items on the list to see more details about it, the backend sends the "full" object. Notice the underlying principle: the UI flow dictates how the storage layer stores objects. This is the anti-thesis to the common wisdom, where the storage layer does not care about the UI, and it's the job of the intermediate layer to transform data for the needs of the UI.
- 3y ago
- denton-scratch 3y ago> the other for the "confirm password" That always pisses me off. I can copy/paste the password from the first field into the second. Hell, I use a password manager; I pasted into the first field, so I paste into the second. It's just a check to make sure they match. But my passwords are complicated enough that I can't remember them long enough to type the characters in the first time; I literally have to copy/paste. So this anti-pattern assumes that users are using some memorable password like their mother's maiden name, probably for all the services they use. Anyone using sane password practices is penalized with stupid friction. [Edit] I have an even bigger gripe about asking me to enter my email address twice. If you really aen't sure I entered it correctly, send me an email asking me to confirm.
- DaiPlusPlus 3y ago> So this anti-pattern assumes that users are using some memorable password like their mother's maiden name, probably for all the services they use. Anyone using sane password practices is penalized with stupid friction. I think you're really, really underestimating how easy it is to make a typo when entering a password into a masked password box, hence the 2 fields. Also, web-browsers don't let you copy-and-paste the password from one box into another - so I don't know how you're doing that: https://security.stackexchange.com/questions/149326/why-cant-we-copy-password-fields https://security.stackexchange.com/questions/149326/why-cant... ----- I appreciate that you're just-as-annoyed with web-form tropes, cliches and irritations as I am - and when I'm building a system or UI for savvy people then sure, I'll do things like skipping the registration form entirely and just use OIDC federation or PassKeys or whatever the current-security-fad-of-the-month is - but my day-job requires me to write software for ...uh... "normal people" and part of that means having to weigh-up the support-costs of users who want a predictable and easy-to-understand that isn't too different from what they already know - and those normal-people are what pays my bills.
- denton-scratch 3y ago> Also, web-browsers don't let you copy-and-paste the password from one box into another Point taken; perhaps simply having that widget that lets you see the password unmasked would be better, than forcing you to enter it blind twice (and getting it wrong twice). Fact is I don't try to copy/paste one field into the other; I paste the same stuff into both. FWIW I'm a normal person. I don't know what OIDC is, and I've never been asked to use Passkeys (I'm proud to know nothing about Apple devices, and I don't entrust my security to "the cloud" or third parties). My password manager is local, backed-up locally. I'm already annoyed once I'm presented with a registration form, and only complete one when I'm forced to. Every extra annoyance increases my rage.