Well it's going slowly. On the one hand I'm excited that this will lead to a really powerful and flexible design. On the other hand I'm finding it diabolically hard to get this code to a reliably unit tested state.
Complicating my life is that I'm using a combination of frappe and the observe package. As it's a polymer app it needs to use observe on the UI facing parts, but also there is nothing similar to ObservableList etc that I can see in frappe. Probably by design as it works with immutable objects, but I need to manipulate a reasonable sized list rendered in core-list so can't afford to change the whole list each time an element changes.
Anyway at a more high level, let me explain what I'm doing.
I'm introducing a new layer to my UI. I have the usual view and view model stuff plus DTO's. What I'm adding now is effectively a client side instance of a REST resource. Unlike the DTO's these are
not anemic domain objects. They handle all the comms with the rest services and expose frappe properties / observe collections of their data. As the data updates (either from an action initiating in the UI or potentially web socket event etc) the properties will update and be picked up by listeners.
I also have higher level domain objects that aggregate data from other domain objects. i.e. they don't correspond 1-1 with a particular REST resource but are derived from them.
The properties they expose are derived from properties of the underlying domain objects. This gets challenging as I'll get to below.
Finally I have view models that are similar to the higher level domain objects in that their data is derived from other models.
I have written a whole bunch of helper functions that I'll make available at some stage for things like
- deriving observable lists from other observable lists. i.e. this is effectively like Stream.map except for observable lists.
- aggregating observable lists together (this is effectively an observable list of observable lists of items that aggregate into an observable list of items). So the items in each list can change and the number of lists being aggregated can change.
- functions for converting between frappe classes and observe classes. For example converting an observable list into a Property<List>
And this is where I spent hours on the weekend pulling my hair out trying to get to unit tested code for. Specifically the problem I was trying to solve was
- I have a status property (Property<Status>) in a higher level domain object that is derived from the status properties of a list of other domain objects.
class DomainObjA {
Property<Status> status;
}
class HigherLevelDomainObj {
ObservableList<DomainObjA> aObjs;
Property<Status> get status; // derived from aObjs->status
}
There's actually a DomainObjB that also has a status that affects the higher level status but I'll leave that out here.
So the question is how to implement HigherLevelDomainObj.status
Remember that aObjs is an observable list so the contents can change over time and the DomainObjA.status is a Property so also changes
Step 1/ I wrote a function that takes an ObservableList and turns it into Property<List>
Step 2/ A function that turns an ordinary List<Property<Foo>> into a Property<List<Foo>>
Step 3/ combine these two functions as follows
ObservableList<Property<Status>> => Property<Property<List<Status>>>
then flatMapLatest on Property<Property<List<Status>>> to give Property<List<Status>>
then I can fold on Property<List<Status>> to create a Property<Status>
The function looks like
Property<List> observableListOfPropertiesToPropertyList(ObservableList<Property> ol) {
Property<List<Property>> plp = observableListToPropertyList(ol);
Property<Property<List>> ppl =
plp.map(listOfPropertiesToPropertyList).asProperty();
Property<List> result = ppl.flatMapLatest((pl) =>
pl.asStream()).asProperty();
return result;
}
My brain exploded at some point trying to debug the unit tests. It seemed to kinda work when the input properties had initial values but not otherwise but they failed in strange ways.
So in summary, I still have a vague hope that this well end up super awesome but definitely not for the feint hearted
A