Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support automatically hydrating nested domain objects when instantiating #5

Closed
uladkasach opened this issue Aug 31, 2020 · 9 comments
Closed
Labels
bug-in-usage-causer Something causes downstream bugs in common use cases

Comments

@uladkasach
Copy link
Member

uladkasach commented Aug 31, 2020

for example:

    user.directories.some((userDir) => serialize(getUniqueIdentifier(new Directory(userDir))) === serialize(getUniqueIdentifier(directory))),

we are required to do new Directory(userDir) in order to use getUniqueIdentifier(), since otherwise userDir is just an object, not an instanceof DomainObject, because when user was instantiated it was instantiated as new User(dbObject)

--

we are told about the DomainObject that each User.directories[number] is - but we are not told about that at run time. Only type defs know that - and those get compiled away :(

is there another way to get the type definitions at run time?

@uladkasach
Copy link
Member Author

uladkasach commented Aug 31, 2020

potentially could use a custom transformer to do this for us. For example, here is a transformer that uses type defs to create functions: https://github.com/woutervh-/typescript-is

although this would require using ttypescript, it may be worth it until transformer plugins are supported out of the box.

so, for example, it would transform our type defs where any time a property is a DomainObject it would add it to the static constructor definition of dependencies = { directories: Directory, contactMethods: ContactMethod }

--

until we have that, we could just support the user defining dependencies manually. Not ideal since we're duplicating information... but would solve this too

@uladkasach
Copy link
Member Author

uladkasach commented Sep 3, 2020

note: per #6, if we have a function that is able to make the type definitions accessible at runtime, we should be able to solve this problem

options:

  • perhaps we can use that to "analyze self" -> in runtime code, have it self analyze to determine what the dependencies are and then hydrate them
  • perhaps we ask the user to generate the definition and leave it somewhere, and then define DomainObject.dependencies=DomainObjectRuntimeTypeDefinition.dependencies
  • perhaps we generate the definitions in a standard spot, and just have the library try to look for the definitions there on its own (e.g., fs.find?) (con - then this would only work server side :/ )

perhaps even, we import the DomainObject class into the ./generated/definition class, and in that file we define DomainObject.dependencies = DomainObjectTypeInformation.dependencies

@uladkasach
Copy link
Member Author

yeah! that works!

import { ClientAccess } from './ClientAccess';

ClientAccess.unique = ['a', 'b', 'c'];

so we can have domain-objects generate type-decorations code, which repeats all the good stuff we have in the TS types in a run time accessible way for the object. (e.g., dependencies, UniqueIdentifier (#2), etc)

it wouldn't even have to generate the full DomainObjectTypeInformation - it can just generate the TypeDecorations. (e.g., /src/models/domainObjects/.generated/decorations.ts) - and then the user just has to make sure they import that one file whenever they use the domain object they're interested in

(e.g., in the /src/models/domainObjects/index.ts)


a problem could arise when users accidentally import domain objects directly, instead of from the index.ts directory. Wonder if there is a way to put "fences" on those exports. (cause if they do that, the decorations wont be imported)


alternatively, we can import the decorations in each model file. (it does suck that its getting a little "magical" now though - since there is an import that is globally affecting other things...)

@uladkasach
Copy link
Member Author

we could also have

import { ClientAccessTypeInformation } from './generated/typeinfo';

class ClientAccess ... {
  public static typeinfo = ClientAccessTypeInformation; 
}
```
and now `typeinfo` is used directly, but that wont help #2 - since we _want_ to have `ClientAccess.UniqueIdentifier = X` set

@uladkasach
Copy link
Member Author

what if the contract was, to still be explicit:

import { decorate } from 'domain-objects';
-vs-
import { decorate } from './generated/decorations';
export class ClientAccess ... {
  public static unique = [];
}
decorate(ClientAccess); // mutates the ClientAccess class; very explicit about mutating. add whatever additional features we want to expose

-vs-

class ClientAccess ... {

}

const ClientAccessDecorated = withDecorations(ClientAccess); // <-- immutable, just extends and assigns... but more confusing and weirder syntax...

export ClientAccessDecorated;

@uladkasach
Copy link
Member Author

so, effectively, decorate would simply automate the process of setting the public static variables such as dependencies and UniqueIdentifier, and whatever else that we could figure out from type defs, but dont have access to at runtime - or could define alternatively, but can't get the types right at run time.

@uladkasach
Copy link
Member Author

uladkasach commented Sep 6, 2020

note: we want to make sure that the error thrown has full context of object if there is validation. i.e.,: validate, THEN hydrate.

@uladkasach
Copy link
Member Author

before the decorate solution is out, we still can support this feature by asking the user to manually define the "nested" domain objects. 75045c1 does that for us

@uladkasach
Copy link
Member Author

done w/ tag v0.5.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug-in-usage-causer Something causes downstream bugs in common use cases
Projects
None yet
Development

No branches or pull requests

1 participant