polygene-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Stanislav Muhametsin <stanislav.muhamet...@zest.mail.kapsi.fi>
Subject Qi4CS and Zest
Date Tue, 30 Jun 2015 20:13:18 GMT
Hello,

As Niclas mentioned earlier in another thread, it is probably best to 
get terminology straight when talking about the Java and C# versions of 
Zest.
For new people or those who just happened to forgot, I am the person who 
"ported" Zest from Java to C#, so I guess I am the right person to 
explain how things are on C# side.
I assume here that readers are at least somewhat familiar with Zest 
codebase and how things work 'under the hood'.

I am going to talk about "Qi4CS" as "Zest ported to C#" since that is 
the current name of the framework (since Zest used to be Qi4j).
There has been talk about Qi4CS moving to be as sub-project of Zest, but 
that will happen after I've refactored the code generation framework 
Qi4CS uses, and when I have time and energy to face the bureaucracy of 
that moving operation.
So for now, "Qi4CS" is basically Zest on C#, and just "Zest" is Zest on 
Java.

My knowledge of Zest codebase might be outdated for some parts, so feel 
free to correct me when I say something that isn't anymore in Zest codebase.
This is quite lengthy mail, but I think and hope that reading all of 
this will create some thoughts and pondering for Zest as well. :)



To start, Qi4CS has the same architectural principle as Zest, namely 
having a Core and then Extensions to interact with other frameworks.
The Qi4CS Core has same components as Zest Core, namely "API", "SPI", 
"Bootstrap", and "Runtime".
They have roughly the same responsibilities as in Zest as well, where 
API types are meant to average every-day user of Qi4CS/Zest, SPI types 
are meant for people writing Extensions/Libraries/etc for Qi4CS/Zest, 
and Bootstrap types are meant for average user to set up a Qi4CS/Zest 
application.
The first difference comes in Runtime component: Zest Runtime is 
"private implementation" of API and SPI components, where Qi4CS Runtime 
is "default implementation" of API and SPI components, additionally 
exposing some types to create more complex Qi4CS Extensions (more of 
that later).

All the main components of Qi4CS Core (and Extensions) are structured by 
having 3 layers: Assembling, Model, and Instance.
The Assembling layer contains types relevant to bootstrap stage of the 
Qi4CS application.
The Model layer contains types relevant to code generation related to 
and verification of Qi4CS application.
Finally, the Instance layer contains types relevant to running and using 
the Qi4CS application, and instantiating and using composites.

The Qi4CS provides two composite types: Plain Composite and Service 
Composite.
The Plain Composite is roughly equivalent to Transient Composite in 
Zest, and Service Composite is exactly the same as in Zest.
There is no Entity Composites in Qi4CS (Core), because I think the 
concept is too application-specific to be included to Core (in the 
projects I've worked, the current entity concepts in Zest are unfeasable 
due to very unusual requirements and design of the environment as a whole).
Nor are there Value Composites either, since I found them to bring 
little new to the Plain Composites.
To counteract the lack of composite types, the Qi4CS Core provides types 
and mechanisms to include new composite types (for example, Entity 
Composite similar to the one in Zest) via Extensions.
These kind of Extensions are the "more complex Qi4CS Extensions" 
mentioned earlier, and the mechanisms to implement this kind of 
functionality are found in Runtime Core component.
On a better side (IMO), the Core is thus much more light-weight, since a 
lot of stuff is now missing from there: all Entity-related things 
(Entity Composite, Entity Stores, Indexing Stores) and Query API/SPI.

Starting to assemble Qi4CS application always starts with the selection 
of suitable architecture.
In Zest, it is assumed that all applications adhere to layered 
architecture, and indeed almost any other architectures can be modeled 
using layered architecture.
The Qi4CS Core works differently, it provides two architectures 
(Singleton and Layered) in Core, and also provides mechanisms and types 
to create custom architectures, should the need arise.
Because of this, the Qi4CS Core actually has one more architectural 
component, in addition to API, SPI, Bootstrap and Runtime: Architectures.
This component corresponds more to the "private implementation" style 
present in Zest Core Runtime, and contains the implementation of 
Singleton and Layered architectures.

During bootstrap stage, the composites in Qi4CS are added to the 
architecture-neutral unit called Assembler.
Singleton architecture has one assembler, accessible directly from the 
main architecture instance.
Layred architecture has layers, each containing modules, and the modules 
then have the assembler.
Notice that unlike Zest, layers and layered architecture itself can not 
hold composite declarations, as they do not have assemblers.
This was initially done for simplicity's sake, and currently there has 
not been any need to change that.
All composite declarations have familiar (for Zest user) functionality 
related to fragments: Mixins, Concerns, Side-Effects and Constraints.
All bootstrap-related types are located in Assembling -layer of Qi4CS 
Core and Extensions.

Once the architecture has been assembled, a model for the application 
and composites is created based on the composite declarations in 
architecture.
Each composite model has a coherent and simple way of examining what 
kind of methods it has, and how various fragments participate in each 
method.
All of the models are verifiable, and creating application instance is 
only possible for verified and valid models.
Like in Zest, there is code generation involved.
However, since CLR type system and the VM in general is much more 
complex than Java, the code generation is optimized to happen during 
compilation stage.
It is possible to generate code programmatically, but one will need the 
"SDK" edition of Qi4CS for that.
All model-related types are located in Model-layer of Qi4CS Core and 
Extensions.

After model is created, validated and the code has been generated, the 
application instance may be created.
Just like in Zest, the Qi4CS application instance has Name, Version and 
Mode (although I am considering removing these, currently there are not 
useful at all, and IIRC they are not present in Zest anymore either?).
And just like in Zest, the Qi4CS application has to be activated before 
it can be used.
All instance-related types are located in Instance-layer of Qi4CS Core 
and Extensions.

The Activation and composite Lifecycle hooks in Zest are accessible by 
implementing Activatable and Initializable (IIRC) interfaces.
The Qi4CS takes different approach: one can annotate the methods on 
fragment types with attributes (C# attributes are roughly same as Java 
annotations) called Activation, Deactivation, and Prototype.
I think this was something that Niclas suggested on Qi4j list ages ago, 
and since I was writing Qi4CS at that point, I decided that instead of 
using interface-implementation-based approach, I would use this 
attribute-based approach.
Personally, I think this approach worked out a lot more better than 
interface-oriented approach, since I can use method parameters to inject 
stuff needed only at certain lifecycle stage (instead of creating and 
injecting to fields).
The "hook" methods will be called by Qi4CS in appropriate lifecycle 
stage of the composite.
Methods marked with Prototype attribute will be called when the 
composite is instantiated from the composite builder.
Methods marked with Activation/Deactivation attribute are only 
meaningful for Service Composites, and will be called when the Service 
Composite is activated/deactivated, respectively.

I've also changed the semantics of @Mixins, @SideEffects and @Concerns 
annotation a bit.
In Qi4CS, they are called DefaultMixins, DefaultSideEffects and 
DefaultConcerns, because the runtime will look up fragments from these 
attributes only if no suitable fragments are provided via "contextful 
methods" (e.g. WithMixins, WithSideEffects, WithConcerns methods found 
on composite declaration during bootstrap stage).
Rationale for this is that IMO the person who actually assembles the 
application should have maximum control over what kind of implementation 
there actually is, and should have ability to "override" the default 
behaviour.

The ModuleInstance in Zest seems to have a lot of responsibilities 
(being architectural unit, also providing composite factories, service 
finders, entity and uow stuff, etc).
In Qi4CS, these responsibilities are moved to a architecture-independent 
unit (on instance level) called Structure Service Provider.
In singleton architecture, the application instance has one SSP directly 
accessible from it.
In layered architecture, each module instance has one SSP accessible 
from it.
The SSP may also be accessed via injections.

The functionality similar to Configuration Composites in Zest is present 
in Qi4CS, but they are not a composite type by themselves.
Instead, there is a ConfigurationManager service, which will create 
ConfigurationInstance<T> plain composites (yes, generic type 
composite!), where T is actual type of configuration composite.
Usually T is other plain composite as well, its state is populated 
during deserialization stage.
The Configuration Composite concept is completely decoupled from Service 
Composite concept.

I also combined the concepts of MetaInfo and @Uses-injection into one in 
Qi4CS.
I found them to be very similar (both are used to pass data from 
"outside" of the Qi4CS/Zest scope), and compacted them into one.
The Uses-injection accepts optional String to distinguish between 
different meaning of the object of same type ("named" objects).
For example, one could have two fields, both of integer type, one with 
[Uses("MyFirst")] and second with just [Uses] attributes.
When e.g. creating composite (or already at bootstrap/model stage), one 
can provide the value for first integer calling .Use("MyFirst", 5) and 
the value for second integer by calling .Use(6) methods.

For model layer, it is sufficient to say that I pretty much wrote that 
from blank state.
Each composite model has Composite Method Models, Special Method Models, 
Constructor Models, and Field Models.
Composite Method Models then each have Parameter Models, Concern Method 
Models, Side Effect Method Models and one Mixin Method Model.
Parameter Models describe possible injections and constraints this 
Composite Method Model has.
Each of the Concern/Side Effect/Mixin Method Model represents a single 
method from a single fragment, and thus the control flow of this 
Composite Method Model can be deduced from those models.
Special Method Models are any method of the fragments marked with 
"special" attribute (e.g. Activation/Deactivation/Prototype discussed 
above), and also have Parameter Models to describe the parameters.
Constructor Models are for each constructor of fragments that the 
composite is made of, also having Parameter Models to describe parameters.
Field Models describe the injected fields of the fragments.

Since C# has concept of properties defined in its language, there is no 
Property<T> in Qi4CS.
Instead, in order to create a property which is part of composite state, 
one does something like "String MyProperty { get; set; }" for interface, 
and then just leaves the implementation out (by having fragments 
implementing that interface leaving the property as abstract).
The Qi4CS detects this situation and during creation of the model, 
delegates the implementation of the setter and getter -methods to the 
generic mixin.
(The similar logic works for C# events, for those familiar of them.)


Phew, what a long mail.
I've most certainly forgot some stuff, but feel free to ask and discuss 
the things I mentioned so far! :)

Mime
View raw message