Genarbaro - Lazy loading

in bt-softdev •  2 years ago 

I am currently working on a project called Genarbaro, which is aimed at collaborative creating a family tree. What sets my project apart is that it stores data in YAML files and sends them to a Git repository on servers - theoretically, because it is still on very early state. To make this possible, I created a library called Gitisto, which acts as an ORM for these types of databases. Yesterday, I added basic support for lazy loading, which significantly improved the performance of the project, also I started some basic cryptographic features a few days ago (We should remember that leaving people cannot be added to the public repository without agreement), but I will describe an idea later in next posts.

Creating a family tree is an essential aspect of understanding our roots and our family's stories. With Genarbaro, I wanted to create a project that would allow users to store their family tree data in a format that is easy to read and edit. I can imagine that in future there will be a big repository like GENI and someone will need to manage to do review of a new records - in this context YAML is the best option. Easy to read, easy to understand. By using YAML files, users can easily work with their family tree data without worrying about the complexity of traditional databases.

Lazy loading is a technique that loads data only when it is needed,
rather than loading everything at once. This technique is essential for
projects like Genarbaro that deal with large amounts of data. Here is how it works.

```tyescript
class MainEntity extends BaseEntity<MainEntity> {
@Property()
somevalue!: string;

@Property()
nested!: NestedEntity;
}
```
```typescript
const entity = await repository.loadEntity(MainEntity, "entity");
// entity.nested.value = 2; // throws an error
await entity.nested.load();
entity.nested.value = 2;
```

I had a few ideas how to implement that but currently I'm using states of BaseEntity class. Previously BaseEntity constructor was responsible for parsing a content string and deserialization. Now constructor assign only basic information like entity id, metadata or version. Loading of the content happens in "load()" method. This method is called automatically when we `repository.loadEntity` but for nested entities only constructor is used:

```typescript
const t = Reflect.getMetadata("design:type", object, field);

if (t.prototype instanceof BaseEntity) {
  const [name, id] = (data[field] as string).split(":");

  return new t(
    this.repository,
    new Version(VersionState.WORKSPACE, ""),
    id
  );
}

```

My base decorator which is creating a list of fields for serialization/deserialization is now also responsible for creating getters and setters for property. That way I can control if it's safe to access data.

```typescript
export function Property(): PropertyDecorator {
return (target, key: string | symbol) => {
const fields = Reflect.getOwnMetadata("fields", target) || [];
if (!fields.includes(key)) {
fields.push(key);
}
Reflect.defineMetadata("fields", fields, target);

const descriptor = {
  get(): any {
    if (!(this as any).loaded) {
      throw new Error("Cannot read not loaded property");
    }
    return (this as any)[\`\_${key.toString()}\`];
  },
  set(val: any) {
    if (!(this as any).loaded) {
      throw new Error("Cannot edit not loaded property");
    }
    (this as any)[\`\_${key.toString()}\`] = val;
  },
  enumerable: true,
  configurable: true,
};
Object.defineProperty(target, key, descriptor);

};
}
```

Many other lines of code has been created. I had to refactor some parts of the code and create new tests. All changes are in attached PR. See you later!

Link to project

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE BLURT!