unitaryfund / metriq-api

RESTful API for Metriq
https://metriq.info
Apache License 2.0
4 stars 8 forks source link

Data transfer objects ("DTOs") #64

Open WrathfulSpatula opened 3 years ago

WrathfulSpatula commented 3 years ago

We have a getSanitized() method for User objects that produces a JSON object appropriate to show members, hiding certain fields like those related to soft deletion. Also, we'll commonly want to populate() our model schema "foreign key"-like references before sending them as API request results.

There's already a standardized concept in software development, for an object for this purpose: a "data transfer object," or "DTO," which is distinct from a "model" schema object.

The basic idea behind a DTO is that it's just "dumb," first of all: it lacks all model object extension logic. (Secondly, a DTO is serializable, though this likely isn't a difficult concern for us to satisfy with any JSON objects we're likely to produce.)

Any time we pass back a model object from the API, we basically call an equivalent to the User model/service getSantized() method to produce a DTO. Further, we should standardize these DTOs, in their own folder.

Whichever of us get to it first, @vprusso, we can produce a folder of userDto, submissionDto, etc., and these will be the result of getSantized() (or getDto()) service methods.

We don't necessarily need a DTO for every model, but we should have one for every model we return as an API call result.

WrathfulSpatula commented 3 years ago

To be clear, my biggest concern at the moment is stripping soft deletion fields from API call results, but also any information that we rightly don't need to tell users. (Ask me, if you're in doubt.)

vprusso commented 3 years ago

I suppose I'm less familiar with this concept so the exact task of what should be done here is still a bit unclear to me.

So we should create service/userDto/ and service/submissionDto/, but it's still not entirely clear what should be placed in these folders. Are we to actually place the JSON result of the respective getSanitized() methods in each of these folders?

WrathfulSpatula commented 3 years ago

@vprusso We can define classes (like for our web app views and components) that are just a list of specific fields that each getSantized() method returns on its result object. These "dumb" classes correspond (mostly one-to-one) with our Mongoose model classes. We instantiate these classes in getSanitized(), and copy model data into them. These DTOs are therefore standardized, lightweight, and safer than directly handling models when we don't need proper models, which could otherwise have side-effects on the database, if we accidentally try to save() via extension method.

These "dumb" classes specifically have no methods, and all their properties are public, (and serializable). (In other languages, these class fields might be allocated on heap, but I don't think the concept applies to JavaScript.)

Maybe I haven't communicated the idea clearly, but maybe the hard part to understand is the "value added" despite the fact that DTOs don't seem like they do anything. DTOs are just classes that standardize "sanitized" API call return types, with the minimum of information we always want to give back to API users the same way, and with no logic or methods.

WrathfulSpatula commented 3 years ago

(They're similar to the simplest cases you see of ad hoc struct definitions in C or C++ for returning multiple values from a method or function.)