which nested data structure should I use for FluidFramework - fluid-framework

If I want to use nested data structure, which data structure is better choose.
eg.
For eg1 case, It seems can not load children DDS data in 'hasInitialized()'.
For eg2, it seems a little complicated.
Is anyone can show some example for this case?

A DataObject contains a collection of one or more DDSes and nesting them allows you to create organizational structure. DDSes themselves can also be nested and retrieved in the initialization steps.
There are a few ways you can solve eg1 and I highlight two below.
For eg2 you're right that it's a bit complicated and generally not a pattern we are pushing at the moment. There are some example in the repo but you will find that they use a slightly different developer model then the HelloWorld. The SimpleComponentEmbed and the Pond are the best starting points if you want to explore DataObject embedding.
Using root SharedDirectory for nesting
The root SharedDirectory allows you to create sub-directories. This is a great way to manage collections of nested maps. Using sub-directories instead of putting a SharedMap on the root ensures creation and setting happens as one atomic action instead of two actions, (one to create and one to set).
The current draw back to this at the moment is that you have to listen to events on the root directory and see if it corresponds to your sub-directory. Eventing on sub-directories is being tracked with Issue #1403
protected async initializingFirstTime() {
const subDirectory = this.root.createSubDirectory("sub-dir");
subDirectory.createSubDirectory("sub-sub-dir");
}
protected async hasInitialized() {
const subDirectory = this.root.getSubDirectory("sub-dir");
const ssDir = subDirectory.getSubDirectory("sub-sub-dir");
this.root.on("valueChanged", (changed: IDirectoryValueChanged) => {
if (changed.path === ssDir.absolutePath) {
const newValue = ssDir.get(changed.key);
console.log(newValue);
}
});
// An easy way to trigger/test the above handler without user interaction
setTimeout(() => {
ssDir.set("foo", "bar2");
}, 10000); // 10 seconds
}
Using Nested DDSes
You can also store nested DDSes. The thing you need to remember is that you are storing/ retrieving the handle to the DDS. In the below example we are creating map1 with a key/value. We are string map1 in map2 which is being stored on the root DDS. We will then get the value from map1 by first getting map2 then map1 then the value.
protected async initializingFirstTime() {
const map1 = SharedMap.create(this.runtime);
map1.set("foo", "bar");
const map2 = SharedMap.create(this.runtime);
map2.set("map1", map1.handle);
this.root.set("map2", map2.handle);
}
protected async hasInitialized() {
const map2 = await this.root.get<IFluidHandle<SharedMap>>("map2").get();
const map1 = await map2.get<IFluidHandle<SharedMap>>("map1").get();
console.log(map1.get("foo"));
}

Related

How to pass nested data structures as properties in LitElement?

In a parent component I have something like:
render() => {
const data = {a:1,b:[1,2,3]}; // of course this is a simplified version of the code
return html`<child-component data=${data}></child-component>`
}
Which is basically equivalent to:
render() => {
const data = {a:1,b:[1,2,3]}; // of course this is a simplified version of the code
return html`<child-component data="[object Object]"></child-component>`
}
Which is basically useless...
Is there a simple way to pass complex object hierarchies into litElement components?
As far as I can tell, my options are:
Option 1. Use attributes: I'm a bit of a litElement noob so I'm not sure if this will work and I'm not sure how to make it work without having to make extra function calls. It would be nice if I could just do all the necessary work inside html.
Research in progress.
Option 2. Use Json.
Stringify the object in the parent component
render() => {
const data = {a:1,b:[1,2,3]}; // of course this is a simplified version of the code
return html`<child-component data=${JSON.stringify(data)}></child-component>`
}
then parse the json in the child component.
This just seems a bit inelegant to me though.
But it works.
In this case what you probably want is to pass the object as a property rather than as an attribute. For complex data such as objects, arrays, functions, etc. that's the preferred method.
You can do it with the following syntax:
render() => {
const data = {a:1,b:[1,2,3]};
// note the period (.), that's the token used to identify that you're passing data as a property
return html`<child-component .data=${data}></child-component>`
}
In general, you should probably give Lit's templating guide a read as some of the most common use cases are covered throughout it.

Can you use a Map instance as an easy-peasy store property?

ie.
const store = {
values: new Map(),
// (gross trivial accessor)
setValue: action( (state, payload) => {
state.values.set(payload.key, payload.value);
}
}
I'm curious because easy-peasy uses a Proxy on the store object (and objects nested within) so that in your action you can safely mutate the state object directly (https://easy-peasy.now.sh/docs/tutorials/primary-api.html#modifying-the-state). I don't know if this also works when using non Plain Old JavaScript Objects, such as Maps.
It looks like this is possible on certain versions, but not without first declaring support for the feature (so the code above will not work right out of the box, as of now). See here for more info: https://github.com/ctrlplusb/easy-peasy/issues/440

How to convert SqlExpression<T> into SqlExpression<TU> with ServiceStack OrmLite?

I need to work with SqlExpression<T> in a private method but the result should be SqlExpression<TU> (due to my project context).
T and TU aims same structure (TU is a subset of T with some computed expressions)
I didn't find a way to convert SqlExpression<T> into SqlExpression<TU>.
In following code T = Product class and TU = Powder class.
// Sample code
private static SqlExpression<Powder> CreateDefaultExpression(IDbConnection db, IPowderListFilter request)
{
var ev = db.From<Product>()
// Joins are set here...
.Select(p => new
{
CustomRalId = Sql.As(p.CustomRalId, nameof(Powder.CustomRalId)),
Mass = Sql.As(Sql.Sum(p.Width * p.Height / 1000000) * 2 * Settings.LacqueringGramsPerSquareMeter / 1000, nameof(Powder.Mass))
});
// I need to do something like...
ev.ConvertTo<SqlExpression<Powder>>();
// or ...
return new SqlExpression<Powder>(ev);
}
You basically can’t, the typed SqlExpression is a query builder that you can’t just change the Type of and have it automatically erase and reapply all the mutations to the query builder using a different Type.
If reuse is the goal you’d need to use standard C# to DRY as much code as possible which won’t be much since all lambda expressions is typed to a different Type.

How to develop an Import/Export Functionality for my node application

I have a configuration application in Nodejs. It has a Component with name and uuid. A Component can have many Schemas. A Schema has a uuid, name, componentId, json. A Schema can have many Configurations. A Configuration has name, schemaId, json and uuid. A Schema can contain reference of many other Schemas in it. Now I want to create a functionality of exporting all the data from one instance of the application and import it in another. What should be the simplest way to do it? a few questions are
How to tell application what to export. for now i think there should be separate arrays for components, schemas and configurations. Like
{
components: ['id1', 'id2'],
schemas: ['s1', 's2'],
configuration: ['c1', 'c2'],
}
this data should be sent to application to return a file with all information that will later be used for importing in another instance
The real question is how should my export file look like keeping in mind that dependencies are also involved and dependencies can also overlap. for example a schema can have many other schemas referenced in its json field. eg schema1 has schema2 and schema4 as its dependencies. so there is another schema schema5 that also require schema2. so while importing we have to make sure that schema2 should be saved first before saving schema1 and schema5. how to represent such file that requires order as well as overlapped dependencies, making sure that schema2 is not saved twice while importing. json of schema1 is shown below as an example
{
"$schema": "http://json-schema.org/draft-04/schema#",
"p1": {
"$ref": "link-to-schema2"
},
"p2": {
"$ref": "link-to-schema4"
},
}
What should be the step wise sudo algorithm i should follow while importing.
This is a perfect occasion for a topological sort.
Taking away components, schemas and configurations terminology, what you have is objects (of various kinds) which depend on other objects existing first. A topological sort will create an order that has only forward dependencies (assuming you don't have circular ones, in which case it is impossible).
But the complication is that you have dependency information in a mix of directions. A component has to be created before its schema. A schema has to be created after the schemas that it depends on. It is not impossible that those schemas may belong to other components that have to be created as well.
The first step is to write a function that takes an object and returns a set of dependency relationships discoverable from the object itself. So we want dependencyRelations(object1 to give something like [[object1, object2], [object3, object1], [object1, object4]]. Where object1 depends on object2 existing. (Note, object1 will be in each pair but can be first or second.)
If every object has a method named uniqueName that uniquely identifies it then we can write a method that works something like this (apologies, all code was typed here and not tested, there are probably syntax errors but the idea is right):
function dependencyInfo (startingObject) {
const nameToObject = {};
const dependencyOf = {};
const todo = [startingObject];
const visited = {};
while (0 < todo.length) {
let obj = todo.pop();
let objName = obj.uniqueName();
if (! visited[ objName ]) {
visited[ objName ] = true;
nameToObject[objName] = obj;
dependencyRelations(obj).forEach((pair) => {
const [from, to] = pair;
// It is OK to put things in todo that are visited, we just don't process again.
todo.push(from);
todo.push(to);
if (! dependencyOf[from.uniqueName()]) {
dependencyOf[from.uniqueName()] = {}
}
dependencyOf[from.uniqueName()] = to.uniqueName();
});
}
}
return [nameToObject, dependencyOf];
}
This function will construct the dependency graph. But we still need to do a topological sort to get dependencies first.
function objectsInOrder (nameToObject, dependencyOf) {
const answer = [];
visited = {};
// Trick for a recursive function local to my environment.
let addObject = undefined;
addObject = function (objName) {
if (! visited[objName]) {
visited[objName] = true; // Only process once.
// Add dependencies
Object.keys(dependencyOf[objName]).forEach(addObject);
answer.push(nameToObject[objName]);
}
};
Object.keys(dependencyOf).forEach(addObject);
return answer;
}
And now we have an array of objects such that each depends on the previous ones only. Send that, and at the other end you just inflate each object in turn.

How to convert a DTO to Domain Objects

I'm trying to apply ubiquitous language to my domain objects.
I want to convert a Data Transfer Object coming from a client into the domain object. The Aggregate's Constructor only accepts the required fields, and the rest of parameters should be passed using aggregate's API even when the Aggregate is being created(by say CreateAggregate command).
But the DTO to Aggregate mapping code becomes a bit messy:
if(DTO.RegistrantType == 0){
registrantType = RegistrantType.Person()
}
elseif(DTO.RegistrantType == 1){
registrantType = RegistrantType.Company()
}
//.....
//.....
var aggregate = new Aggregate(
title,
weight,
registrantType,
route,
callNumber,
)
//look at this one:
if(DTO.connectionType == 0){
aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}
//..........
//..........
One thing I should mention is that this problem doesn't seem a domain specific problem.
How can I reduce these If-Else statements without letting my domain internals leakage, and with being sure that the aggregate(not a mapping tool) doesn't accept values that can invalide it's business rules, and with having the ubiquitous language applied?
Please don't tell me I can use AoutoMapper to do the trick. Please read the last part carefully.'
Thank you.
A typical answer would be to convert the DTO (which is effectively a message) into a Command, where the command has all of the arguments expressed as domain specific value types.
void doX(DTO dto) {
Command command = toCommand(dto)
doX(command)
}
void doX(Command command) {
// ...
aggregate.Route(command.connectionType)
}
It's fairly common for the toCommand logic use something like a Builder pattern to improve the readability of the code.
if(DTO.connectionType == 0){
aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}
In cases like this one, the strategy pattern can help
ConnectionTypeFactory f = getConnectionFactory(DTO.connectionType)
ConnectionType connectionType = f.create(DTO)
Once that you recognize that ConnectionTypeFactory is a thing, you can think about building lookup tables to choose the right one.
Map<ConnectionType, ConnectionTypeFactory> lookup = /* ... */
ConnectionTypeFactory f = lookup(DTO.connectionType);
if (null == f) {
f = defaultConnectionFactory;
}
So why don't you use more inheritance
for example
class CompanyRegistration : Registration {
}
class PersonRegistraiton : Registration {
}
then you can use inheritance instead of your if/else scenario's
public class Aggregate {
public Aggregate (CompanyRegistration) {
registantType = RegistrantType.Company();
}
public Aggregate (PersonRegistration p) {
registrantType = RegistrantType.Person();
}
}
you can apply simmilar logic for say a setRoute method or any other large if/else situations.
Also, i know you don't want to hear it, you can write your own mapper (inside the aggegate) that maps and validates it's business logic
for example this idea comes from fluentmapper
var mapper = new FluentMapper.ThatMaps<Aggregate>().From<DTO>()
.ThatSets(x => x.title).When(x => x != null).From(x => x.title)
It isn't too hard to write your own mapper that allow this kind of rules and validates your properties. And i think it will improve readability

Resources