Gang,

Le 2017-05-21 19:45, Tibor Mlynarik a écrit :
I have another one. Is it ok to report here or better create JIRA issue ?

What Niclas said, JIRAs are better. Keep them coming Tibor!


My custom IdentityGenerator ( inherited from layer hierarchy ) is
probably overwritten with default one.
My guess is that ModuleAssemblyImpl.addDefaultAssemblers() is not
respecting hierarchy inheritance.

I have custom IdentityGenerator declared in BottomLayer but used in UpperLayer.


The issue is not about type hierarchy but structure and visibility.

Default assemblers are added to ALL modules for
- IdentityGenerator,
- Serialization
- and UnitOfWorkFactory
if no service assignable to each was assembled.

This makes sense for UnitOfWorkFactory, UnitOfWorkFactory is bound to its module and one always create an uow from a given
module.
But it doesn't for the two other types for which one will want to get the closest in the app structure instead.

I think this mixes two use cases.

Assembly of all modules must contain a service of a given type. That's the UnitOfWorkFactory case and current
"default assemblers" do the job.

If a service of a given type cannot be found, then fallback to a framework default. That's the IdentityGenerator and Serialization cases. Both because they are core api services (e.g. in Module) and almost all extensions and a bunch of libraries use them. But assembling them in all modules prevent reasonable assemblies, as I see it it's a regression. And requiring users to assemble some special services in all their modules feels wrong.

In 2.1, serialization and metrics have a fallback, identity generation doesn't:
- Module.identityGenerator() throws NoSuchServiceException if not found
- Module.valueSerialization() instantiate OrgJsonValueSerialization directly and cache - Module.metricsProvider() is package protected, it instantiate MetricsProviderAdapter directly and cache

On develop: metrics have a fallback, the others don't:
- Module.identityGenerator() throws NoSuchServiceException if not found, but is always found (default assemblers) - Module.serialization() throws NoSuchServiceException if not found, but is always found (default assemblers) - Module.metricsProvider() instantiate MetricsProviderAdapter directly and cache
but the first two are assembled in all modules anyway.

A noop implementation is a good fit for metrics.
UuidGeneratorMixin is core and a simple class so it could easily be instantiated directly as a fallback. Our default serialization requires composition but it could be reworked to be instantiatable, requiring injection.

But these services are not bound to their assembly module, so they could be shared across modules. Declaration in all modules is cheap, but it pollutes the application model in all modules, which is not nice.

And returning plain Objects where one would expect services feels weird to me. Plus it makes getting these core
services injected in composites impossible.

If my assembly misses these core services, it's very easy to fall into the cryptic assembly error trap. In most cases you want to fix the assembly issue holistically according to your application needs.
That's how 2.x works.

The intent of "default assemblers" for IdentityGenerator and Serialization is convenience. When starting an application or writing Polygene tests one wants simple defaults. But the way they are done now gets in the way when assembling non trivial applications.

One way to provide these fallbacks while respecting the structure and visibility concept, for the sake of least surprise, would be to add an arbitrary layer with these default services with application visibility and make all other "leaf" layers use this arbitrary layer. Effectively making these default services visible to the whole application while allowing any custom implementations assembled to override them.

We could also add a way to opt-out of this during assembly, for a stricter structure.

Metrics could then work the same way for the sake of least surprise again, still providing a noop implementation.

I gave the idea a try in the `pm/bootstrap/support-layer` branch, it passes the whole test suite. I opened a GitHub PR so it's easier to look at: https://github.com/apache/polygene-java/pull/6/files

The controversial aspect of this solution is that it automatically adds an arbitrary layer to support Polygene
core services that an application can override.
But I feel that's far less invasive than adding several assembly declarations to all modules.
And it makes structure reflect the real intent.

Here are some Envisage screenshots to illustrate the change:
- current `develop` behaviour: https://pasteboard.co/9hJfzPf4r.png
- with the added `polygene-runtime` layer: https://pasteboard.co/9hJJnHL4h.png - with explicit assembly of base services in the application assembly, the `polygene-runtime` layer is not needed so
it is not added to the assembly: https://pasteboard.co/9hKatWHMc.png

WDYAT?

Reply via email to