The thing that really shocked me about this video (well one of them, I was really searching for security and authentication) was a pattern shown here to create references between "server" entities.
class A
{
String idB;
B b;
}
A very intuitive approach, but it creates duplicate fields on A, and since B also has its own id field then it creates redundant information. To avoid this you can use what I'll call the Reference Pattern and as a class can be represented (in Dart) as
abstract class Ref extends DbObj
{
String get href;
}
Now, if you make both A and B inherit from Ref then, they they could be defined aswhere DbObj is
The idea is that in JSON a Ref this looks something likeclass DbObj extends Resp { String id; }
{ "id" : "someId" "href" : "http://someHost/someResource/{id} }
class A extends Ref
{
String someField;
B b;
String get href => "http://someHost/A/$id";
}
class B extends Ref
{
String someOtherField;
String get href => "http://someHost/B/$id";
}
Where is the magic? The important thing is that when you store an A object on MongoDB, remember to only store a blank B object inside that only contains its own id field. Now you can retrieve it, encode it, and the client can receive it as a JSON like this
{
"someField" : "whatever",
"b" : {
"id" : "id_of_B",
"href" : "http://someHost/B/id_of_B"
},
"id" : "id_of_A",
"href" : "http://someHost/A/id_of_A"
}
}
Notice 2 things:
- You can plug the href's directly into a request, src tag, or whatever, making it super easy to get, put, or delete the entity.
- You can expand "b" to get the full object after making a get request to the its "href" and store it back on "b" again. Also, you can send a partial or complete B object from the beginning without have to create an extra structure to compensate.
{
"someField" : "whatever",
"b" : {
"id" : "id_of_B",
"href" : "http://someHost/B/id_of_B",
"someOtherField" : "Nice!"
},
"id" : "id_of_A",
"href" : "http://someHost/A/id_of_A"
}
They key is: all your structures inherit from `Ref` so they all contain the minimal data needed to retrieve/modify them, but they can be expanded. I other words, your object are isomorphic!
If you've read carefully you'd notice that the title takes about a "Response" and there is an undefined "Resp" class. This Resp class have the purpose of encapsulating the possibility of all requests to the server: failure. Ok, you can let the server crash and send back a 4xx or 5xx status, then on the client you have to catch that error on the request, but things might get messy. Another option is to send back a JSON with the error, something like
{"error" : "not found"}
then decode the JSON to an object and just check for the error. Here is the implementation of the Resp class
class Resp
{
bool get success => nullOrEmpty(error);
bool get failed => ! success;
String error;
}
Since Ref extends DbObj which extends Resp, all your objects can also warn you about errors and you can write beautiful code like this (which also uses Darts async/await syntax)
A a = await getA ();
a.b = await getRequest (a.b.href);
if (a.b.failed)
{
print (a.b.error);
return;
}
//Do something with a.b