The first question I always get asked:
Do you support round-tripping of the generated code your factory creates? In other words, do you support a developer going into the generated code, modifying it in an arbitrary way, and then read back those code changes and their intended meanings into the models used by the factory?
[Quite often the asking party has the handy Class Diagram functionality of Visual Studio in mind when they ask this.]
It works like this for our particular factory (the EFx Factory):
The factory user designs the logical components of the applications and services in architectural layers using a logical model explorer and visual designers. Each component is part of an overall (combined) meta-model that has a set of metadata (properties) associated to the elements in it. This metadata defines things like the naming, structure and concerns of the component within the architectural layer and ultimately governs how that component should be implemented. This diagram shows one of the designers used in this process.
Of course, these metadata ‘properties’ provide an abstraction to the actual generated solution artefacts and it is defined in architectural terms, since we don’t want the factory users to have to be expert C# solution domain experts to use the models. That’s on of the powers of using abstraction your factory.
[As a side note: In this factory, we do allow and support (even facilitate) the developers hand-crafting the business logic in source code, since we don’t feel we can sufficiently model business logic successfully or completely for all cases to suit all solutions. The hand-crafted custom source code is referenced by the models and maintained separately from the generated code, so won’t be considered as part of this discussion.]
At any given point in the design time of the logical models, assuming certain constraints and certain validation rules are satisfied, source code and other artefacts are generated from the model and designated to the appropriate container (project or solution folder) in the physical solution. It is then subsequently compiled into assemblies at a later stage.
The answer for this factory is: No
We are not saying it can’t be done; just saying that the considerable effort required to support this is wasted when:
- The Model is King. The model is intended to represent the architecture, its components and the architectural patterns it implements. In this factory, all the components (with business logic components being the exception) are modelled in the architecture and configurable to suit certain identified variability. This ‘specification’ is meant to define the specification of the solution, not the source code it generates. Since those interested in the abstractions it provides will not necessarily be interested (or skilled in reading) low level source code. The model is king, and the authority on the actual state of the factory product. All analysis and generation tools read and write to these models.
- Abstraction assumed to be high: In this factory, the generated solution artefacts that represent the physical incantation of the models is provided by pluggable providers (we call them ‘Artefact Generators’ for obvious reasons). These providers are intended to implement a pattern or technology to satisfy a set of concerns for a set of components at a particular layer of the architecture. For example, we would have one such provider for a service to implement the Service Contract and Data Contracts using a given technology, say – WCF. Now these providers themselves may be mini-factories (‘factorettes‘ as we call them) with their own abstractions that remove the requirements for the factory user to be experts in implementing their best practices or patterns in specific technology terminology or using a low level tool – like source code. The providers are expected to hide that complexity, and only expose a simple interface to configure it, in generally understandable terms and patterns. So, the abstractions they provide may not be as simple as perhaps the Class Designer in Visual Studio (which could be said to have little to no abstraction).
- One-way mapping assumed. In following with the previous point, but more importantly, since the providers are to implement a technology based upon a meta-model of components and metadata in a well defined architectural model, they are encouraged to provide more than one set of patterns for their technology which vary and are each optimized depending on the configuration of that meta-model. You can imagine this being the same way that source code compilers implement different byte code sequences depending on how the developer wrote their source code. One pattern may not be most optimal for all configurations. So how do you determine from the modified source code which pattern is in use, and how that change in source code should be reflected in the model? It’s the same problem most people see when reverse engineering assemblies using a reflector. You never get back the original source code with its abstractions in place – because there is a one to many mapping between source code and compiled code.
The next point is (philosophical)?
This factory does not want to promote source code development, favouring more powerful abstractions, visual modelling and extensibility (except, as we mentioned above, for the business logic – which we do want the factory users to define in source code). So, we don’t expect the developers to tweak the generated source code.
Having said that, there are legitimate reasons they might want to do this – customization. The assumption here is that; in modifying the generated source code your intention is to define a variability point (or configuration of a variability point) that the factory does not cater for currently. In these cases, this deliberate action is interpreted as a case for further factory customization – which is a valid reason to create a variant of the factory to accommodate this variability. Its part of the natural development cycle of a factory.
Just as an interesting point of strategy to identify these cases, this factory employs a validation rule that checks the last generated source code artefacts for modifications, before re-generation. Then, if any modifications are found, the generation process is halted and the factory user is notified of the change. In those cases the modification needs to be resolved with an appropriate manual action by the factory user in order for generation to continue.