NestJs: dynamic module IoC scope - nestjs

How does the framework manage the lifetime of DynamicModules?
The NestJs documentation on Modules states that:
In Nest, modules are singletons by default, and thus you can share the same instance of any provider between multiple modules effortlessly.
How can you share multiple dynamic module instances between modules?
The NestJs documentation on DynamicModules states that:
In fact, what our register() method will return is a DynamicModule. A dynamic module is nothing more than a module created at run-time, with the same exact properties as a static module, plus one additional property called module.
How can you manage/change the scope of DynamicModules? For example, changing them from behaving transitively to as a singleton. Defining their injection token, retrieving them on demand, etc.

How does the framework manage the lifetime of DynamicModules?
Generally speaking, like it does any other module. A dynamic module is just a special name for a module configuraed by a function and represented by an object. The end result is usually something like
{
module: SomeModuleClass,
imports: [Imports, For, The, Module],
providers: [SomeProviderToken, SomeProviderService, ExtraProvidersNeeded],
exports: [SomeProviderService],
}
Pretty much the same kind of thing you'd see in an #Module() decorator, but configured via a function that possibly uses DI instead of just written directly
How can you share multiple dynamic module instances between modules?
I might need a bit of clarification here, but I'll be happy to edit my answer with more detail once I know what's the goal here, or what you're trying to do.
How can you manage/change the scope of DynamicModules? For exmaple, changing them from behaving transitively to as a singleton. Defining their injection token, retrieving them on demand, etc.
The easiest option for sharing your configuration (besides making the module #Global()) is to make a wrapper module that re-exports the dynamic module once it has been configured.
Example: Let's say we have a dynamic FooModule that we want to pass application to, to designate the name of the application, and we want to re-use that module in several other places
#Module({
imports: [
FooModule.forRoot(
{ application: 'StackOverflow Dynamic Module Scope Question' }
)
],
exports: [FooModule],
})
export class FooWrapperModule {}
Now instead of importing FooModule.forRoot() again in multiple places, we just import FooWrapperModule and get the same instance of FooService with the configuration originally passed.
I do want to mention that by convention, DynamicModule.forRoot/Async() usually implies single time registration in the RootModule and usually has a #Global() or isGlobal: true config attached to it somewhere. This isn't always the case, but it holds relatively true.
DynamicModule.register/Async() on the other hand, usually means that we are configuring a dynamic module for this module only and it can be reconfigured elsewhere to have it's own separate config. This can lead to cool setups where you can have multiple JwtService instances that have different secret values for signing (like for an access and refresh token signing service).
Then there's DynamicModule.forFeature() which is like register in that it is at a per module basis, but usually it uses config from the forRoot/Async() call that was already made. The #nestjs/typeorm module, mikro-orm/nestjs module, and #ogma/nestjs-module module are three separate examples I can think of that follow this pattern. This is a great way to allow for general configuration at the root level (application name, database connection options, etc) and then allow for scoped configuration at the module level (what entities will be injected, logger context, etc)

Related

Is there a recommended approach to referencing an instantiated class object in React / Nextjs?

Let's say I have a Node.js library that I need to instantiate in my React app.
const awesomeLibrary = new AwesomeLibrary('someID', options);
awesomeLibrary allows me to do things like awesomeLibrary.configure(), awesomeLibrary.specifyCustomProperty() and awesomeLibrary.fireEvent().
I would like to instantiate it once and then call those methods from the instantiated reference (awesomeLibrary) around my application. What is the best way to that?
Would I instantiate it in top-level entry component (e.g. _app.tsx), export it, import it into other components and call the methods? In order to export it, I'd have to instantiate it outside the component, instead of in the lifecycle of the component. Is that a bad practice?
Alternatively, could I relegate the instantiation to a hook? Say useAwesomeLibrary() in which I instantiate and export the methods? But wouldn't doing that create an instance every time the hook is called from different components?
I'm really not sure how to handle this.
Instantiating class before exporting it is a fairly common practice and one of the possible implementations of singleton in JavaScript/React. For example, you can use popular i18next library in this way:
services/i18n.js:
import i18n from 'i18next';
i18n.init({
// pass translations and other config
});
export default i18n;
_app.js:
import i18n from 'services/i18n';
// snip
<I18NextProvider i18n={i18n} /> // consume already instantiated i18n object
The difference between this and instantiating object in a hook is WHEN it will be instantiated. When you do this in hook, it will instantiate every time you mount component with this hook. When in separate file (module), then it will be once per page load.
Is that a bad practice? I don't know, it depends on how you'd use it. There is nothing bad in doing this per se, and even it has its advantages (in the example above we delegated responsibility of creating and configuring i18n service from _app.js, which is very good from Single Responsibility Principle perspective). But keep in mind that codebase bloated with singletons will probably become hard to maintain.

Puppet: Class Ordering / Containment - always wrong order

I read a lot about ordering puppet classes with containment (iam using Puppet 6). But it still does not work for me in one case. Maybe my english is not good enough and i miss something. Maybe somebody know what iam doing wrong.
I have a profile to installing a puppetserver (profile::puppetserver). This profile has three sub-classes which I contain within the profile::puppetserver
class profile::puppetserver(
) {
contain profile::puppetserver::install
contain profile::puppetserver::config
contain profile::puppetserver::firewall
}
That works fine for me. Now I want to expand this profile and install PuppetDB. For this, i use the puppetdb module from puppet forge:
So what i do is add profile::puppetserver::puppetdb and the contain to the profile::puppetserver
class profile::puppetserver::puppetdb(
) {
# Configure puppetdb and its underlying database
class { 'puppetdb': }
# Configure the Puppet master to use puppetdb
class { 'puppetdb::master::config': }
}
When i provision my puppetserver first and add the profile::puppetserver::puppetdb after it, puppetdb installs and everything works fine.
If I add it directly with contain, and provisioning everything at once, it crashes. It's because the puppetdb module is installed randomly during my master server installs (and also the postgresql server and so on). That ends in my puppetserver is not running and my puppetdb generate no local ssl certificates and the service doesn't comes up.
What i try first:
I installed the puppetdb Package in my profile::puppetserver::puppetdb directly and use the required flag. It works when i provision all at once.
class profile::puppetserver::puppetdb (
) {
Package { 'puppetdb':
ensure => installed,
require => Class['profile::puppetserver::config']
}
}
So i think i could do the same in the code above:
class profile::puppetserver::puppetdb(
) {
# Configure puppetdb and its underlying database
class { 'puppetdb':
require => Class['profile::puppetserver::config']
}
# Configure the Puppet master to use puppetdb
class { 'puppetdb::master::config':
require => Class['profile::puppetserver::config']
}
}
But this does not work...
So i read about puppet class containment and ordering by chains. So i did this in my profile::puppetserver
class profile::puppetserver(
) {
contain profile::puppetserver::install
contain profile::puppetserver::config
contain profile::puppetserver::firewall
contain profile::puppetserver::puppetdb
Class['profile::puppetserver::install'] ->
Class['profile::puppetserver::config'] ->
Class['profile::puppetserver::firewall'] ->
Class['profile::puppetserver::puppetdb']
}
But it still does not have any effect... he still starts to install postgresql and the puppetdb package during my "puppetserver provisioning" in the install, config, firewall steps.
How i must write the ordering, that all things from the puppetdb module, which i call in profile::puppetserver::puppetdb, only starts when the rest of the provisioning steps are finished?
I really don't understand it. I think maybe it haves something to do with the fact, that i declare classes from the puppetdb module inside of profile::puppetserver::puppetdb and not the directly Resource Type. Because when i use the Package Resource Type with the Require Flag, it seems to work. But i really don't know how to handle this. I think there must be a way or?
I think maybe it haves something to do with the fact, that i declare
classes from the puppetdb module inside of
profile::puppetserver::puppetdb and not the directly Resource Type.
Because when i use the Package Resource Type with the Require Flag, it
seems to work.
Exactly so.
Resources are ordered with the class or defined-type instance that directly declares them, as well as according to ordering parameters and instructions applying to them directly.
Because classes can be declared multiple times, in different places, ordering is more complicated for them. Resource-like class declarations such as you demonstrate (and which you really ought to avoid as much as possible) do not imply any particular ordering of the declared class. Neither do declarations via the include function.
Class declarations via the require function place a single-ended ordering constraint on the declared class relative to the declaring class or defined type, and declarations via the contain function place a double-ended ordering constraint similar to that applying to all resource declarations. The chaining arrows and ordering metaparameters can place additional ordering constraints on classes.
But i really dont know how to handle this. I think there must be a way or?
Your last example shows a viable way to enforce ordering at the level of profile::puppetserver, but its effectiveness is contingent on each of its contained classes taking the same approach for any classes they themselves declare, at least where those third-level classes must be constrained by the order of the second-level classes. This appears to be where you are falling down.
Note also that although there is definitely a need to order some things relative to some others, it is not necessary or much useful to try to enforce an explicit total order over all resources. Work with the lightest hand possible, placing only those ordering constraints that serve a good purpose.

Java 9 module and double dispatch in DDD

On some of my projects, I use the double dispatch mechanism to provide at run time a "view" of my infrastructure module to my domain module (Strengthening your domain: The double dispatch pattern). What I call 'modules' above is simply separate jar files where the dependency from service.jar->domain.jar is enforced at compile time only. Will I be able to have this working on java-9 if I define my service and domain as 'true' java 9 modules?
module domain
L Fee.java
L Payment recordPayment(double, BalanceCalculator)
L BalanceCalculator.java
module service
L BalanceCalculatorImpl.java // implements BalanceCalculator
L double calculate(Fee fee) //call fee.recordPayment(amount,this)
Yes that is possible. Here are some things to consider:
The module domain needs to export the package containing Fee. Possibly to everyone but at least to service.
The module service will have to require domain as BalanceCalculatorImpl has to access BalanceCalculator since it implements it.
It looks like clients of service need to know about domain as well, which is a textbook case for implied readability.
In a simple setup either service or some third module will have to instantiate BalanceCalculatorImpl and pass it to Fee, this can not happen in domain or it would create a cyclic dependency.
A more advanced solution would be services, where all code that can access BalanceCalculator, even inside domain, can get hold of all its implementations.
Taking all of these into account, this is what the two module declarations might look like:
module com.example.domain {
// likely some requires clauses
// export packages containing Fee and BalanceCalculator
exports com.example.domain.fee;
exports com.example.domain.balance;
}
module com.example.service {
requires public com.example.domain;
// likely some more requires clauses
// expose BalanceCalculatorImpl as a service,
// which makes it unnecessary to export the containing package
provides com.example.domain.balance.BalanceCalculator
with com.example.service.balance.BalanceCalculatorImpl;
}
Then every module that likes to use BalanceCalculator can declare it in its module declaration with uses com.example.domain.balance.BalanceCalculator and get instances of that using Java's ServiceLoader.
You can find more practical applications of the module system (particularly for services] in a demo I created.
(Note: The answer was revised after this exchange.)

Use a puppet module multiple times

I'm using a puppet module from Puppet Forge - https://forge.puppet.com/creativeview/mssql_system_dsn
The documentation indicates to use it like this:
class {'mssql_system_dsn':
dsn_name => 'vcenter',
db_name => 'vcdb',
db_server_ip => '192.168.35.20',
sql_version => '2012',
dsn_64bit => true,
}
I need to create multiple odbc data sources.
However, if I simply duplicate this snippet twice and change the parameters I get a multiple declaration error.
How can I declare this module multiple times?
How can I declare this module multiple times?
You cannot do so without modifying the module. Although it is possible to declare the same class multiple times if you use include-like syntax, that does not afford a means to use different parameters with different declarations. This is all connected to the fact that Puppet classes are singletons. I can confirm based on a quick review of the module's code that its design does not support defining multiple data sources.
I'd encourage you to file an enhancement request with the module author. If that does not quickly bear fruit, then you have the option of modifying the module yourself. It looks like that would be feasible, but not as simple as just changing a class keyword to define.
As the author didn't answer my request and had not merged a pull request from another contributor I created my own module;
https://forge.puppet.com/garfieldmoore/odbc_data_source
If anyone is interested enough to review my module's code and offer improvements or let me know when I have not followed best practises I would appreciate it

Is it a convention to have a minimal init.pp that class-scopes the module?

I've come across the following convention, the init.pp is as minimal as possible and looks like this for the example of a java8 module in modules/java8/init.pp
import "*"
class java8 {
include java8::java8
}
Then a modules/java8/java8.pp defines the actual rules/implementations:
class java8::java8 {
# ...
}
Is this a convention, is it an old convention and deprecated? What would or is the rational behind this?
I'm not familiar with that style as any widely-used convention, and I see only limited value to it. Specifically, it appears to serve as a compromise between code organization interests and usage interests: it allows that every class of consequence will be defined in a manifest file named after it (including the delegate main class, java8::java8, in modules/java8/manifests/java8.pp), while providing a main class for the module with a one-segment qualified name (java8), so that users can simply
include 'java8'
I think it's fairly common nowadays to keep the main class small by making it delegate the details to other, private, classes inside the module, but I don't see much value in delegating to exactly one other class for (apparently) naming purposes alone. I also think it's potentially confusing to have different classes with the same unqualified name (java8) in the same module.

Resources