[Model-Driven Software Design] "Domain Architecture" Component Domain Architecture

        How to build an appropriate domain architecture for an existing target and platform architecture. This chapter looks at general best practices for building DSLs, building transformation architectures, and details on a few technology-focused topics.

1. DSL structure

1. Choose a suitable DSL

       When trying to find a DSL for a domain, one should always consider the variability that needs to be expressed, so before committing to a sound graphics-like textual or graphical DSL, one should ask oneself whether a simpler form of DSL is adequate.

      If only routine configuration needs to be done, then a simple properties file or wizard may be sufficient to represent the variability needed to describe a product. If more freedom is required—the variable space from which one has to choose becomes larger—smooth or tree-based configurations are more suitable, while feature models become the most functionally following a fundamentally configuration-oriented approach. Powerful DSLs.

Various ways to build a DSL 

2. Configuration and construction--variation

        Now looking at two kinds of DSLs: DSLs that support creative constructs and DSLs that provide a means of configuration, it's interesting to combine the two.

      All kinds of data structures can be defined using the metamodel.

       Now suppose you want to describe variants of your model. For example, in the context of people and addresses, the following variants might make sense:

  •   A party can have one or more addresses;
  •   A party may or may not store phone numbers;
  •  Construction is a phone number and wants to store the country code;
  • Addresses may have a state field to represent in the United States.

       To express these variants, a feature model—a conventional configuration DSL—can be overlaid on a model built with an inventive construction DSL. Selecting or deselecting a feature on a feature model results in the inclusion or removal of a model element. Certain model elements in the structural model must be associated with features in the configuration model.

3. Modeling behavior

        A DSL describes structural aspects of a software system, such as component structures, persistence maps, or input formats. However, most systems also have structural aspects that need to be modeled.

       The easiest way to model behavior is to reduce it to simple descriptive labels if necessary.

       Another relatively simple way of describing behavior is to specify a particular kind of behavior using a well-known formalism. Classic examples of that approach are statecharts, other examples

4. Specific grammatical issues

       From a model transformation or code generation point of view, as long as there is a way to transform it into an abstract representation that works with generators, and as long as the metamodel properly expresses the concepts that should be modeled by the DSL, the concrete syntax of the DSL is not really The problem. But the DSL is the "user interface" to the metamodel - that is, the developer of the application must be able to read, write and understand the model correctly. This is a very important issue that cannot be ignored. After all, the idea of ​​MDSD is to provide more efficient means of expressing domain concepts: this efficiency depends largely on the concrete syntax.

     When considering the specific syntax, remember that the model is generally not "drawn" on a flipchart, but rather in an interactive editor. That means maybe features like zooming, panning, context menus and buttons or collapsing at will.

5. Continuous verification of metamodel

Simple Component Metamodel 

     The development of a formal metamodel of a domain and the importance of continuous validation are illustrated. Examination of the metamodel must be done with the help of domain experts. The development team must be able to use it smoothly and without misunderstandings.

     Whenever there is something that cannot be easily expressed in the vocabulary of the metamodel, it means that the expression does not conform to the metamodel, the metamodel is wrong, or it is inaccurate.

     This technique is very popular in domain modeling. This technology becomes the ubiquitous language.

Second, the general transformation of the architecture

1. Which part of the target architecture should be generated

     In the context of domain architecture there are always artifacts generated which on the one hand are not included in the platform and on the other hand cannot be described well and concisely with a DSL.

     The introduction to the constructs of typical opportunistic programming languages ​​shows that "generation" has been studied in depth, such as loops in DSLs. The DSL should be mostly declarative and not mutate into a classical programming language - if that's the case, you should actually use a programming language and centralize the handwritten code in the generated code.

2. Believe in regeneration

     The final implemented application should be created with a fully automated build process including regeneration of all generated/transformed artifacts. . Once a separate manual step is required as part of the business process, or a single line of source code has to be manually modified after regeneration, it is only a matter of time before MDSD drops support for traditional non-generated development, because manually modifying generated Artifacts are tedious and error-prone.

      This doesn't mean that an application should -- or have to -- be 100% generated. As long as 3GL is suitable for this purpose, it is perfectly possible to continue to implement parts of it with 3GL. This best practice only states that those parts of the build must be fully built so that the complete system can be recreated in a single loop. Subsequent modifications to the generated artifacts are unrestricted.

3. Development model

       To avoid duplication and minimize the amount of manual labor, as much information contained in a model should be explored as possible. This means that usually more than source code is generated: build and configuration scripts, skeleton/fixtures for component testing, and possibly even test implementation, test data and mock objects, database definition scripts, data migration and cluster scripts, Simple GUI for control and maintenance of data or testing of partial documents.

 1) Generation of composition configuration

     The DSL can be used as a "configuration language" for the architecture, so there are limits to which the architecture and the DSL are compatible with each other. For complex platforms, these configurations are usually not source codes, but generalized configuration files that are often expressed in XML. In most cases, these configurations can be easily generated from the model, since the necessary information is often already included.

2) Architecture of supporting system

       You cannot build any system by focusing only on the architecture of software. A systematic system structure is always required to describe the existing machines, processes and the software assigned to them.

4. Generate beautiful code -- whenever possible

       It is unrealistic to assume that developers will never see the generated code. Even if developers don't need to change the generated code, for example by inserting handwritten parts, they will still face it if they debug the generated application with traditional tools, or if they have to check the configuration of the generator.

    Using this best practice is important to help developers embrace code generation.

    The basic statement underpinning this best practice applies not only to generated code, but also to handwritten code: source code is no longer primarily abbreviated for machines, but intended to be read by other consumers of source code -- people .

5. Model-driven integration

      In many cases, it is necessary to integrate the systems developed by oneself with MDSD into existing systems and infrastructures. Software projects that are actually isolated are rare. In most cases, software is developed within the context of an existing system, ie with a remaining lifetime ahead. Furthermore, over time, developers often wish to gradually replace legacy systems with newer and better appropriate functionality, which should be implemented with modern technology.

     Integration generally includes systematic mapping of various APIs, as well as required data and protocol transformation between these APIs. Depending on the integration strategy, integration code must be inserted in the application being developed, or in an existing legacy application to be integrated. Required artifacts often include appropriate data transformation scripts for one-time use.

6. Separation of generated and non-generated code

      When only part of the application is being built -- as is often the case these days -- those gaps must be filled in with handwritten code. However, modifying the generated code hides a lot of issues about consistency, build management versioning and consistency between the model and the generated source code, largely due to repeated regeneration, even though this last aspect is Controlled by - from a proprietary technical point of view - modern builders.

     If the files with the generated code are never modified, the generated code can be easily deleted if necessary, and a complete regeneration can be done. If the generated code has to be manually modified, this may only occur in those places that are specifically designated not to be overwritten during regeneration.

7. Module transformation

     To enable the reuse of transforms, it is recommended to modularize transforms. Depending on the transformation language used, this can be done with structured or object-oriented programming concepts, as in classical programming. Among them are subroutines/procedures, classes, or loosely coupled components that are then responsible for generating different layers or aspects of the target architecture. They can also be muddied individually. The way these are structured is usually tool-specific, but is very important.

8. Cascading model-driven development

    Model-to-model transformations are useful for modularizing a transformation process. However, the question remains as to which steps a potentially complex transformation process should be broken down into. MDA uses the notations PIM and PSM to provide guidance for these decisions: one should model one's business logic in PIM, then convert it to PSM, and finally generate code from it.

     Start with architecture-centric MDSD first, which means providing MDSD support for your own software architecture. The metamodel contains the basic building blocks of the architecture and a model that describes the system from a technical point of view. Once this infrastructure has been built, other layers of MDSD-infrastructure can be layered on top of it.

3. Establishing the Technical Aspects of Transformation

1. The display integration of the generated code and the manual implementation part

     Explicit integration means that the generated code is completely independent of the handwritten code at the outset. Developers are responsible for integrating these artifacts. These two kinds of code are actually completely independent - the non-generated code often depends on the generated code, because they are usually used together in a system - this is rare, very rare.

    The easiest way to integrate is to create protected areas in the generated code where developers can insert handwritten code. These guard areas are specified in such a way that they can be read by the generator, so that handwritten code will not be rewritten during subsequent runs of the generator.

    UML tools generally work this way. Here, class stubs are generated from the model data, and developers then integrate behavior into the class stubs.

2. Dummy code

    Developers often force certain things in order to achieve a consistent system. In the context of a 3-tier implementation, for example, developers are required to implement a class of their own that satisfies the following requirements:

  • A special naming convention must be followed;
  • A certain class must be inherited, and if necessary, specific operations must be overridden;
  • A specific interface must be implemented, otherwise specific predefined operations must be provided;

    Because the classes are being developed by hand here, they cannot be simply enhanced via code-generated templates. Then, what can be done generates code that can check for required features with the help of the compiler.

3. Technology subdomain

       Large systems generally contain a wide variety of aspects. It is complex and impractical to describe all these aspects in a single model. Models are in danger of being overloaded with details in different ways and too much. Furthermore, in most cases a DSL is suitable for describing one particular aspect, but not for other aspects of the system.

     The DSL identifies persistent model elements so that the required code and RDBMS schema can be generated. It is difficult to accommodate all of this information in a single model. A UML-based language cannot always cover all these aspects.

4. Agent elements

     In principle, integrating different subdomains with webmaster metaclasses works well, but it will lead to specific occurrences in learning multiple models, whether the model is that of different subdomains or, for example, a model of shared elements used as an excuse in various parts. model elements. In order to avoid the skin texture of the message, it is often recommended to use a proxy or drink.

5. External model markup

     In order to be able to transform a source model into a target model, it is sometimes necessary to provide additional information appropriate to the specific target model at generation time. Adding this information to the source model would unnecessarily influence it with the concepts of the target model. OMG recommends using model markup, but few tools fully support this concept.

6. Aspect positioning and MDSD

       Aspect-Oriented Programming AOP. According to AOP, aspects are crosscutting concerns that traverse code horizontally. Representative examples are often of a technical nature, such as transactions, persistence, or logging. The goal of aspect localization is to localize these cross-cutting concerns within a single module within the context of a family of software systems and make it easier to change or configure.

     Several specific aspects can be easily implemented with a generator:

  • thread synchronization
  • Resource allocation
  • Safety

7. Descriptive meta objects

     When using a rich domain-specific platform for MDSD, in order to be able to dynamically control the platform, applications often require information about model elements at runtime.

8. Generated reflective layer

      A meta-object protocol is a way to inspect, modify, or "reinterpret" programming language objects. This usually happens dynamically. In the context of MDSD, at least a read-only meta-object protocol can be provided, so that these classes can be inspected internally or operations can be called dynamically. This kind of work has nothing to do with whether the underlying programming language supports reflection or other similar mechanisms: it is possible to implement this method in C/C++ too! A common interface allows client programs to access various generated classes.

Fourth, the use of interpreter

     Most of the discussion is ideological about MDSD with code generation. It is arguably the most widespread approach to MDSD, but interpreters are a different approach, sharing the same basic principles and meeting the definition of "automatically transforming formal models into executable code", Although in a different way.

      Interpreters and generators are functionally equivalent. Each model can be used as input to any other model, at least in principle. It is more common, however, to use generators for the structural aspects of a system and interpreters for the behavior. The rationale behind this is that the structural aspects are well organized.

1. Interpreter

      An interpreter is a piece of software that, at runtime, reads a model and evaluates it, performing any operations specified in the model. Like a generator, an interpreter acts on the formal model with precisely defined semantics, and using both techniques it can perform operations specified in the model.

2. Revisit MDSD terminology

1) domain

     We define a domain as a restricted area of ​​interest or knowledge, intentionally making the term broadly applicable. Clearly this definition makes no assumptions about using an interpreter or a generator for a given domain.

2) Metamodel

    Either metamodel can be used as the basis for a generator or interpreter.

3) Meta meta model

      The parsers for interpreters and generators are the same, so their meta-metamodels are the same - at least in principle.

4) Formal model

   Any model with a concrete grammar can be used as input to both the generator and the interpreter. Behavior-rich textual models, however, are a domain in which interpreters in particular can show their prowess, as they facilitate the use of simple primitives with slightly different semantics derived from those of the underlying programming languages.

5) Platform

   Both generators and interpreters are based on platform code -- code that already exists on the target platform -- and it's generally a good idea to use as much of that platform code as possible. However, the interaction between the interpreter and the platform looks different from the interaction between the generator and the platform.

3. Interpret the non-functional properties of the program

         So when should a generative approach be used, and when is an interpreter more appropriate?

  1) Performance

2) Code size

3) Binding time

4. Integrate interpreter in one system

      To be of much help, the interpreter must be integrated into the rest of the system. An interpreter contacts the rest of the world through three points, namely the interface through which the interpreter can be invoked, the extension points through which the system enables specific functionality to be invoked by a model, and the mechanisms through which the model can be provided to the interpreter.

   1) Call the interpreter

   An interpreter is a piece of code written in the platform's programming language so that it can be called like any other piece of code. An interpreter typically provides a simple interface with essentially a single method, treating objects as parameters passed to the interpreted model -- and returning the model's execution results.

2) Extension points

   It is often not feasible to define an easily understandable metamodel that addresses all aspects of a domain. Doing that often introduces significant additional complexity, since simplicity is one of the reasons for doing MDSD after all. It is therefore best practice to define extension points in the metamodel that allow a model to refer to code written in the base programming language.

3) Provide the model

    An interpreter needs a model to execute at runtime, so the application must provide it. Models for interpreters are usually text documents, which opens up more possibilities for their storage and provisioning.

     The easiest way is to configure them as part of the system.

     Another alternative is to store the models as external resources and have the system retrieve them at runtime.

5. Interpret procedures and tests

      Like any other piece of code, the interpreter needs to be tested, and like MDSD of any nature, there are two flavors of testing.

1).Test interpreter

      Interpreters tend to be easier to test than generators because they don't require generated code. In fact, interpreters can be tested with the most basic unit tests in a straightforward manner, especially if they deal with textual models.

    The most common approach is to use black-box testing, which feeds the interpreter all into one model and then examines the results.

2). Test model

   Models can be tested with standard unit tests, which call the interpreter through its own formal interface and check the results or impact.

     But an added benefit of the interpreter is that the model can be changed without restarting the system, which makes the system exhibit the modified behavior immediately. This makes it possible to combine debugging with part-domain requirements engineers, where both parties alternately modify the model until the system behaves as required.

Guess you like

Origin blog.csdn.net/zhb15810357012/article/details/131186159