diff --git a/documentation/architecture.asciidoc b/documentation/architecture.asciidoc index cacf1c87..61173dc5 100644 --- a/documentation/architecture.asciidoc +++ b/documentation/architecture.asciidoc @@ -3,7 +3,7 @@ toc::[] :idprefix: :idseparator: - -There are many different views on what is summarized by the term _architecture_. First we introduce the xref:key-principles[key principles] and xref:architecture-principles[architecture principles] of devonfw. Then we go into details of the the xref:application-architecture[architecture of an application]. +There are many different views that are summarized by the term _architecture_. First, we will introduce the xref:key-principles[key principles] and xref:architecture-principles[architecture principles] of devonfw. Then, we will go into details of the the xref:application-architecture[architecture of an application]. == Key Principles For devonfw we follow these fundamental key principles for all decisions about architecture, design, or choosing standards, libraries, and frameworks: @@ -25,9 +25,9 @@ We follow a strictly component oriented design to address the following sub-prin ** http://en.wikipedia.org/wiki/Separation_of_concerns[Separation of Concerns] ** http://en.wikipedia.org/wiki/Reusability[Reusability] and avoiding http://en.wikipedia.org/wiki/Redundant_code[redundant code] ** http://en.wikipedia.org/wiki/Information_hiding[Information Hiding] via component API and its exchangeable implementation treated as secret. -** _Design by Contract_ for self-contained, descriptive, and stable component APIs. +** _Design by Contract_ for self-contained, descriptive, and stable component APIs. ** xref:technical-architecture[Layering] as well as separation of business logic from technical code for better maintenance. -** _Data Sovereignty_ (and _high cohesion with low coupling_) says that a component is responsible for its data and changes to this data shall only happen via the component. Otherwise maintenance problems will arise to ensure that data remains consistent. Therefore interfaces of a component that may be used by other components are designed _call-by-value_ and not _call-by-reference_. +** _Data Sovereignty_ (and _high cohesion with low coupling_) says that a component is responsible for its data and changes to this data shall only happen via the component. Otherwise, maintenance problems will arise to ensure that data remains consistent. Therefore, interfaces of a component that may be used by other components are designed _call-by-value_ and not _call-by-reference_. * *Homogeneity* + Solve similar problems in similar ways and establish a uniform link:coding-conventions.asciidoc[code-style]. @@ -42,24 +42,24 @@ For the architecture of an application we distinguish the following views: * The Infrastructure Architecture describes an application from the operational infrastructure perspective. It defines the nodes used to run the application including clustering, load-balancing and networking. This view is not explored further in this guide. === Business Architecture -The _business architecture_ divides the application into _business components_. A business component has a well-defined responsibility that it encapsulates. All aspects related to that responsibility have to be implemented within that business component. Further the business architecture defines the dependencies between the business components. These dependencies need to be free of cycles. A business component exports his functionality via well-defined interfaces as a self-contained API. A business component may use another business component via its API and compliant with the dependencies defined by the business architecture. +The _business architecture_ divides the application into _business components_. A business component has a well-defined responsibility that it encapsulates. All aspects related to that responsibility have to be implemented within that business component. Further, the business architecture defines the dependencies between the business components. These dependencies need to be free of cycles. A business component exports its functionality via well-defined interfaces as a self-contained API. A business component may use another business component via its API and compliant with the dependencies defined by the business architecture. As the business domain and logic of an application can be totally different, the devonfw can not define a standardized business architecture. Depending on the business domain it has to be defined from scratch or from a domain reference architecture template. For very small systems it may be suitable to define just a single business component containing all the code. === Technical Architecture -The _technical architecture_ divides the application into technical _layers_ based on the http://en.wikipedia.org/wiki/Multilayered_architecture[multilayered architecture]. A layer is a unit of code with the same category such as service or presentation logic. A layer is therefore often supported by a technical framework. Each business component can therefore be split into _component parts_ for each layer. However, a business component may not have component parts for every layer (e.g. only a presentation part that utilized logic from other components). +The _technical architecture_ divides the application into technical _layers_ based on the http://en.wikipedia.org/wiki/Multilayered_architecture[multilayered architecture]. A layer is a unit of code with the same category such as a service or presentation logic. So, a layer is often supported by a technical framework. Each business component can therefore be split into _component parts_ for each layer. However, a business component may not have component parts for every layer (e.g. only a presentation part that utilized logic from other components). An overview of the technical reference architecture of the devonfw is given by xref:img-t-architecture[figure "Technical Reference Architecture"]. It defines the following layers visualized as horizontal boxes: * link:guide-client-layer.asciidoc[client layer] for the front-end (GUI). -* link:guide-service-layer.asciidoc[service layer] for the services used to expose functionality of the +* link:guide-service-layer.asciidoc[service layer] for the services used to expose functionality of the back-end to the client or other consumers. * link:guide-batch-layer.asciidoc[batch layer] for exposing functionality in batch-processes (e.g. mass imports). * link:guide-logic-layer.asciidoc[logic layer] for the business logic. * link:guide-dataaccess-layer.asciidoc[data-access layer] for the data access (esp. persistence). -Also you can see the (business) components as vertical boxes (e.g. _A_ and _X_) and how they are composed out of component parts each one assigned to one of the technical layers. +Also, you can see the (business) components as vertical boxes (e.g. _A_ and _X_) and how they are composed out of component parts each one assigned to one of the technical layers. Further, there are technical components for cross-cutting aspects grouped by the gray box on the left. Here is a complete list: @@ -82,11 +82,11 @@ developers a sound orientation within the project. Further, the architecture diagram shows the allowed dependencies illustrated by the dark green connectors. Within a business component a component part can call the next component part on the layer directly below via a dependency on its API (vertical connectors). -While this is natural and obvious it is generally forbidden to have dependencies upwards the layers +While this is natural and obvious, it is generally forbidden to have dependencies upwards the layers or to skip a layer by a direct dependency on a component part two or more layers below. The general dependencies allowed between business components are defined by the xref:business-architecture[business architecture]. In our reference architecture diagram we assume that the business component `A1` is allowed to depend -on component `A2`. Therefore a use-case within the logic component part of `A1` is allowed to call a +on component `A2`. Therefore, a use-case within the logic component part of `A1` is allowed to call a use-case from `A2` via a dependency on the component API. The same applies for dialogs on the client layer. This is illustrated by the horizontal connectors. Please note that link:guide-jpa.asciidoc#entity[persistence entities] are part of the API of the data-access component part so only the logic component part of the same business component may depend on them. @@ -96,7 +96,7 @@ The technical architecture has to address non-functional requirements: * *scalability* + is established by keeping state in the client and making the server state-less (except for login session). Via load-balancers new server nodes can be added to improve performance (horizontal scaling). * *availability* and *reliability* + -are addressed by clustering with redundant nodes avoiding any single-point-of failure. If one node fails the system is still available. Further the software has to be robust so there are no dead-locks or other bad effects that can make the system unavailable or not reliable. +are addressed by clustering with redundant nodes avoiding any single-point-of failure. If one node fails the system is still available. Further, the software has to be robust so there are no dead-locks or other bad effects that can make the system unavailable or not reliable. * *security* + is archived in the devonfw by the right templates and best-practices that avoid vulnerabilities. See link:guide-security.asciidoc[security guidelines] for further details. * *performance* + diff --git a/documentation/coding-conventions.asciidoc b/documentation/coding-conventions.asciidoc index 57c6d0f8..a65b7885 100644 --- a/documentation/coding-conventions.asciidoc +++ b/documentation/coding-conventions.asciidoc @@ -15,7 +15,7 @@ Besides general Java naming conventions, we follow the additional rules listed h * Names of Generics should be easy to understand. Where suitable follow the common rule `E=Element`, `T=Type`, `K=Key`, `V=Value` but feel free to use longer names for more specific cases such as `ID`, `DTO` or `ENTITY`. The capitalized naming helps to distinguish a generic type from a regular class. == Packages -Java Packages are the most important element to structure your code. We use a strict packaging convention to map technical layers and business components (slices) to the code (See link:architecture.asciidoc#technical-architecture[technical architecture] for further details). By using the same names in documentation and code we create a strong link that gives orientation and makes it easy to find from business requirements, specifications or story tickets into the code and back. +Java Packages are the most important element to structure your code. We use a strict packaging convention to map technical layers and business components (slices) to the code (See link:architecture.asciidoc#technical-architecture[technical architecture] for further details). By using the same names in documentation and code we create a strong link that gives orientation and makes it easy to find from business requirements, specifications or story tickets into the code and back. For an devon4j based application we use the following Java-Package schema: [source] @@ -40,7 +40,7 @@ Please note that for library modules where we use `com.devonfw.module` as `«bas We combine the above naming and packaging conventions to map the entire architecture to the code. This also allows tools such as https://github.com/devonfw/cobigen[CobiGen] or https://github.com/devonfw/sonar-devon4j-plugin/[sonar-devon4j-plugin] to "understand" the code. -Also this helps developers going from one devon4j project to the next to quickly understand the code-base. +Also this helps developers going from one devon4j project to the next one to quickly understand the code-base. If every developer knows where to find what, the project gets more efficient. A long time ago maven standardized the project structure with `src/main/java`, etc. and turned chaos into structure. With devonfw we experienced the same for the codebase (what is inside `src/main/java`). @@ -131,7 +131,7 @@ With devonfw we experienced the same for the codebase (what is inside `src/main/ ---- == Code Tasks -Code spots that need some rework can be marked with the following tasks tags. These are already properly pre-configured in your development environment for auto completion and to view tasks you are responsible for. It is important to keep the number of code tasks low. Therefore every member of the team should be responsible for the overall code quality. So if you change a piece of code and hit a code task that you can resolve in a reliable way do this as part of your change and remove the according tag. +Code spots that need some rework can be marked with the following tasks tags. These are already properly pre-configured in your development environment for auto completion and to view tasks you are responsible for. It is important to keep the number of code tasks low. Therefore, every member of the team should be responsible for the overall code quality. So if you change a piece of code and hit a code task that you can resolve in a reliable way, please do this as part of your change and remove the according tag. === TODO Used to mark a piece of code that is not yet complete (typically because it can not be completed due to a dependency on something that is not ready). @@ -154,7 +154,7 @@ A FIXME tag is added by the author of the code or someone who found a bug he can A REVIEW tag is added by a reviewer during a code review. Here the original author of the code is responsible to resolve the REVIEW tag and the reviewer is assigning this task to him. This is important for feedback and learning and has to be aligned with a review "process" where people talk to each other and get into discussion. In smaller or local teams a peer-review is preferable but this does not scale for large or even distributed teams. == Code-Documentation -As a general goal the code should be easy to read and understand. Besides clear naming the documentation is important. We follow these rules: +As a general goal, the code should be easy to read and understand. Besides, clear naming the documentation is important. We follow these rules: * APIs (especially component interfaces) are properly documented with JavaDoc. * JavaDoc shall provide actual value - we do not write JavaDoc to satisfy tools such as checkstyle but to express information not already available in the signature. @@ -167,10 +167,10 @@ As a general goal the code should be easy to read and understand. Besides clear This section gives you best practices to write better code and avoid pitfalls and mistakes. === BLOBs -Avoid using `byte[]` for BLOBs as this will load them entirely into your memory. This will cause performance issues or out of memory errors. Instead use streams when dealing with BLOBs. For further details see link:guide-blob-support.asciidoc[BLOB support]. +Avoid using `byte[]` for BLOBs as this will load them entirely into your memory. This will cause performance issues or out of memory errors. Instead, use streams when dealing with BLOBs. For further details see link:guide-blob-support.asciidoc[BLOB support]. === Closing Resources -Resources such as streams (`InputStream`, `OutputStream`, `Reader`, `Writer`) or transactions need to be handled properly. Therefore it is important to follow these rules: +Resources such as streams (`InputStream`, `OutputStream`, `Reader`, `Writer`) or transactions need to be handled properly. Therefore, it is important to follow these rules: * Each resource has to be closed properly, otherwise you will get out of file handles, TX sessions, memory leaks or the like * Where possible avoid to deal with such resources manually. That is why we are recommending `@Transactional` for transactions in devonfw (see link:guide-transactions.asciidoc[Transaction Handling]). @@ -191,7 +191,7 @@ try { The code above is wrong as in case of an `IOException` the `InputStream` is not properly closed. In a server application such mistakes can cause severe errors that typically will only occur in production. As such resources implement the `AutoCloseable` interface you can use the `try-with-resource` syntax to write correct code. The following code shows a correct version of the example: [source,java] ---- -try (InputStream in = new FileInputStream(file)) { +try (InputStream in = new FileInputStream(file)) { readData(in); } catch (IOException e) { throw new IllegalStateException("Failed to read data.", e); @@ -209,10 +209,10 @@ When catching exceptions always ensure the following: === Lambdas and Streams With Java8 you have cool new features like lambdas and monads like (`Stream`, `CompletableFuture`, `Optional`, etc.). -However, these new features can also be misused or lead to code that is hard to read or debug. To avoid pain, we give you the following best practices: +However, these new features can also be misused or led to code that is hard to read or debug. To avoid pain, we give you the following best practices: -. Learn how to use the new features properly before using. Often developers are keen on using cool new features. When you do your first experiments in your project code you will cause deep pain and might be ashamed afterwards. Please study the features properly. Even Java8 experts still write for loops to iterate over collections, so only use these features where it really makes sense. -. Streams shall only be used in fluent API calls as a Stream can not be forked or reused. +. Learn how to use the new features properly before using. Developers are often keen on using cool new features. When you do your first experiments in your project code you will cause deep pain and might be ashamed afterwards. Please study the features properly. Even Java8 experts still write for loops to iterate over collections, so only use these features where it really makes sense. +. Streams shall only be used in fluent API calls as a Stream can not be forked or reused. . Each stream has to have exactly one terminal operation. . Do not write multiple statements into lambda code: + @@ -225,7 +225,7 @@ return foo; }).collect(Collectors.toList()); ---- + -This style makes the code hard to read and debug. Never do that! Instead extract the lambda body to a private method with a meaningful name: +This style makes the code hard to read and debug. Never do that! Instead, extract the lambda body to a private method with a meaningful name: + [source,java] ---- @@ -246,7 +246,7 @@ set.stream().flatMap(x -> x.getChildren().stream()).filter(this::isSpecial).coll set.stream().collect(Collectors.toList()).forEach(...) // bad set.stream().peek(...).collect(Collectors.toList()) // fine ---- -. Lambda parameters with Types inference +. Lambda parameters with Types inference + [source,java] ---- @@ -314,12 +314,12 @@ id = Optional.ofNullable(fooCto).map(FooCto::getBar).map(BarCto::getBar).map(Bar === Encoding Encoding (esp. Unicode with combining characters and surrogates) is a complex topic. Please study this topic if you have to deal with encodings and processing of special characters. For the basics follow these recommendations: -* When you have explicitly decide for an encoding always prefer Unicode (UTF-8 or better). This especially impacts your databases and has to be defined upfront as it typically can not be changed (easily) afterwards. +* When you have explicitly decided for an encoding always prefer Unicode (UTF-8 or better). This especially impacts your databases and has to be defined upfront as it typically can not be changed (easily) afterwards. * Do not cast from `byte` to `char` (Unicode characters can be composed of multiple bytes, such cast may only work for ASCII characters) -* Never convert the case of a String using the default locale (esp. when writing generic code like in devonfw). E.g. if you do `"HI".toLowerCase()` and your system locale is Turkish, then the output will be "hı" instead of "hi" what can lead to wrong assumptions and serious problems. If you want to do a "universal" case conversion always use explicitly an according western locale (e.g. `toLowerCase(Locale.US)`). Consider using a library (https://github.com/m-m-m/util/blob/master/core/src/main/java/net/sf/mmm/util/lang/api/BasicHelper.java) or create your own little static utility for that in your project. +* Never convert the case of a String using the default locale (esp. when writing generic code like in devonfw). E.g. if you do `"HI".toLowerCase()` and your system locale is Turkish, then the output will be "hı" instead of "hi", which can lead to wrong assumptions and serious problems. If you want to do a "universal" case conversion always use explicitly an according western locale (e.g. `toLowerCase(Locale.US)`). Consider using a library (https://github.com/m-m-m/util/blob/master/core/src/main/java/net/sf/mmm/util/lang/api/BasicHelper.java) or create your own little static utility for that in your project. * Write your code independent from the default encoding (system property `file.encoding`) - this will most likely differ in JUnit from production environment ** Always provide an encoding when you create a `String` from `byte[]`: `new String(bytes, encoding)` -** Always provide an encoding when you create a `Reader` or `Writer` : `new InputStreamReader(inStream, encoding)` +** Always provide an encoding when you create a `Reader` or `Writer` : `new InputStreamReader(inStream, encoding)` === Prefer general API Avoid unnecessary strong bindings: diff --git a/documentation/guide-batch-layer.asciidoc b/documentation/guide-batch-layer.asciidoc index f3e95a06..057892fe 100644 --- a/documentation/guide-batch-layer.asciidoc +++ b/documentation/guide-batch-layer.asciidoc @@ -3,30 +3,30 @@ toc::[] = Batch Layer -We understand batch processing as bulk-oriented, non-interactive, typically long running execution of tasks. For simplicity we use the term batch or batch job for such tasks in the following documentation. +We understand batch processing as a bulk-oriented, non-interactive, typically long running execution of tasks. For simplicity, we use the term "batch" or "batch job" for such tasks in the following documentation. -devonfw uses link:http://projects.spring.io/spring-batch/[Spring Batch] as batch framework. +devonfw uses link:http://projects.spring.io/spring-batch/[Spring Batch] as a batch framework. -This guide explains how Spring Batch is used in devonfw applications. It focuses on aspects which are special to devonfw if you want to learn about spring-batch you should adhere to springs references documentation. +This guide explains how Spring Batch is used in devonfw applications. It focuses on aspects which are special to devonfw. If you want to learn about spring-batch you should adhere to springs references documentation. -There is an example of simple batch implementation in the https://github.com/devonfw/my-thai-star/tree/develop/java/mtsj/batch[my-thai-star batch module]. +There is an example of a simple batch implementation in the https://github.com/devonfw/my-thai-star/tree/develop/java/mtsj/batch[my-thai-star batch module]. -In this chapter we will describe the overall architecture (especially concerning layering) and how to administer batches. +In this chapter, we will describe the overall architecture (especially concerning layering) and how to administer batches. == Layering -Batches are implemented in the batch layer. The batch layer is responsible for batch processes, whereas the business logic is implemented in the logic layer. Compared to the link:guide-service-layer.asciidoc[service layer] you may understand the batch layer just as a different way of accessing the business logic. -From a component point of view each batch is implemented as a subcomponent in the corresponding business component. +Batches are implemented in the batch layer. The batch layer is responsible for batch processes, whereas the business logic is implemented in the logic layer. Compared to the link:guide-service-layer.asciidoc[service layer], you may understand the batch layer just as a different way of accessing the business logic. +From a component point of view, each batch is implemented as a subcomponent in the corresponding business component. The business component is defined by the link:architecture.asciidoc[business architecture]. Let's make an example for that. The sample application implements a batch for exporting ingredients. This ingredientExportJob belongs to the dishmanagement business component. So the ingredientExportJob is implemented in the following package: -//Example doesn't exist anymore and I didn't find any other used batches. +//Example doesn't exist anymore and I didn't find any other used batches. [source] .dishmanagement.batch.impl.* -Batches should invoke use cases in the logic layer for doing their work. +Batches should invoke use cases in the logic layer for doing their work. Only "batch specific" technical aspects should be implemented in the batch layer. ========================== @@ -37,16 +37,16 @@ The batch calls the use case "create product" in the logic layer for actually cr === Directly accessing data access layer -In practice it is not always appropriate to create use cases for every bit of work a batch should do. Instead, the data access layer can be used directly. +In practice, it is not always appropriate to create use cases for every bit of work a batch should do. Instead, the data access layer can be used directly. An example for that is a typical batch for data retention which deletes out-of-time data. -Often deleting out-dated data is done by invoking a single SQL statement. It is appropriate to implement that SQL in a link:guide-repository.asciidoc[Repository] or link:guide-dao.asciidoc[DAO] method and call this method directly from the batch. -But be careful that this pattern is a simplification which could lead to business logic cluttered in different layers which reduces maintainability of your application. -It is a typical design decision you have to take when designing your specific batches. +Often deleting, out-dated data is done by invoking a single SQL statement. It is appropriate to implement that SQL in a link:guide-repository.asciidoc[Repository] or link:guide-dao.asciidoc[DAO] method and call this method directly from the batch. +But be careful: this pattern is a simplification which could lead to business logic cluttered in different layers, which reduces the maintainability of your application. +It is a typical design decision you have to make when designing your specific batches. == Project structure and packaging -Batches will be implemented in a separate Maven module to keep the application core free of batch dependencies. The batch module includes a dependency to the application core-module to allow reuse of the use cases, DAOs etc. -Additionally the batch module has dependencies to the required spring batch jars: +Batches will be implemented in a separate Maven module to keep the application core free of batch dependencies. The batch module includes a dependency on the application core-module to allow the reuse of the use cases, DAOs etc. +Additionally the batch module has dependencies on the required spring batch jars: [source,xml] ---- @@ -96,7 +96,7 @@ To allow an easy xref:start-batch[start of the batches] from the command line it com.devonfw.application.mtsj.SpringBootApp bootified - + @@ -126,7 +126,7 @@ java -jar -batch--bootified.jar --spring.main.web-application-type [%header] |=== |Parameter |Explanation -|`--spring.main.web-application-type=none`| This disables the web app (e.g. Tomcat) +|`--spring.main.web-application-type=none`| This disables the web app (e.g. Tomcat) |`--spring.batch.job.names=`| This specifies the name of the job to run. If you leave this out ALL jobs will be executed. Which probably does not make to much sense. |``| (Optional) additional parameters which are passed to your job |=== @@ -145,7 +145,7 @@ In real world scheduling of batches is not as simple as it first might look like For devonfw we propose the batches themselves should not mess around with details of scheduling. Likewise your application should not do so. This complexity should be externalized to a dedicated batch administration service or scheduler. This service could be a complex product or a simple tool like cron. We propose link:http://rundeck.org[Rundeck] as an open source job scheduler. - + This gives full control to operations to choose the solution which fits best into existing administration procedures. == Handling restarts @@ -241,7 +241,7 @@ public class SimpleAuthenticationTasklet implements Tasklet { } ---- -The username and password have to be supplied via two cli parameters `-username` and `-password`. This implementation creates an "authenticated" `Authentication` and sets in the Spring Security context. This is just for demonstration normally you should not provide passwords via command line. The actual authentication will be done automatically via Spring Security as in your "normal" application. +The username and password have to be supplied via two cli parameters `-username` and `-password`. This implementation creates an "authenticated" `Authentication` and sets in the Spring Security context. This is just for demonstration normally you should not provide passwords via command line. The actual authentication will be done automatically via Spring Security as in your "normal" application. If you have a more complex authentication mechanism in your application e.g. via OpenID connect just call this in the tasklet. Naturally you may read authentication parameters (e.g. secrets) from the command line or more securely from a configuration file. In your Job Configuration set this tasklet as the first step: diff --git a/documentation/guide-component-facade.asciidoc b/documentation/guide-component-facade.asciidoc index 0711123f..3b1c4918 100644 --- a/documentation/guide-component-facade.asciidoc +++ b/documentation/guide-component-facade.asciidoc @@ -3,7 +3,7 @@ toc::[] = Component Facade -For each component of the application the link:guide-logic-layer.asciidoc[logic layer] defines a component facade. +For each component of the application, the link:guide-logic-layer.asciidoc[logic layer] defines a component facade. This is an interface defining all business operations of the component. It carries the name of the component (`«Component»`) and has an implementation named `«Component»Impl` (see xref:implementation[implementation]). @@ -22,7 +22,7 @@ public interface Bookingmanagement { BookingCto findBookingCto(Long id); Page findBookingEtos(BookingSearchCriteriaTo criteria); - + void approveBooking(BookingEto booking); } @@ -31,7 +31,7 @@ public interface Bookingmanagement { == Implementation The implementation of an interface from the link:guide-logic-layer.asciidoc[logic layer] (a component facade or a link:guide-usecase.asciidoc[use-case]) carries the name of that interface with the suffix `Impl` and is annotated with `@Named`. An implementation typically needs access to the persistent data. -This is done by link:guide-dependency-injection.asciidoc[injecting] the corresponding link:guide-repository.asciidoc[repository] (or link:guide-dao.asciidoc[DAO]). +This is done by link:guide-dependency-injection.asciidoc[injecting] the corresponding link:guide-repository.asciidoc[repository] (or link:guide-dao.asciidoc[DAO]). According to link:architecture.asciidoc#architecture-principles[data-sovereignty], only repositories of the same business component may be accessed directly. For accessing data from other components the implementation has to use the corresponding API of the logic layer (the component facade). Further, it shall not expose persistent entities from the link:guide-dataaccess-layer.asciidoc[dataaccess layer] and has to map them to link:guide-transferobject.asciidoc[transfer objects] using the link:guide-beanmapping.asciidoc[bean-mapper]. diff --git a/documentation/guide-component.asciidoc b/documentation/guide-component.asciidoc index 1a790081..b4e82be5 100644 --- a/documentation/guide-component.asciidoc +++ b/documentation/guide-component.asciidoc @@ -20,13 +20,13 @@ Cross-cutting aspects belong to the implicit component `general`. It contains te == Business Component The link:architecture.asciidoc#business-architecture[business-architecture] defines the business components with their allowed dependencies. A small application (microservice) may just have one component and no dependencies making it simple while the same architecture can scale up to large and complex applications (from bigger microservice up to modulith). Tailoring an business domain into applications and applications into components is a tricky task that needs the skills of an experienced architect. -Also the tailoring should follow the business and not split by technical reasons or only by size. +Also, the tailoring should follow the business and not split by technical reasons or only by size. Size is only an indicator but not a driver of tailoring. -Whatever hypes like microservices are telling you, never get mislead in this regard: +Whatever hypes like microservices are telling you, never get misled in this regard: If your system grows and reaches `MAX+1` lines of code, it is not the right motivation to split it into two microservices of `~MAX/2` lines of code - such approaches will waste huge amounts of money and lead to chaos. == App Component -Only in case you need cross-cutting code that aggregates other component you may introduce the component `app`. +Only in case you need cross-cutting code that aggregates another component you may introduce the component `app`. It is allowed to depend on all other components but no other component may depend on it. With the modularity and flexibility of spring you typically do not need this. However, when you need to have a class that registers all services or component-facades using direct code dependencies, you can introduce this component. @@ -36,4 +36,4 @@ The following class diagram illustrates an example of the business component `St image::images/guide-logic-layer.png["logic layer component pattern",scaledwidth="80%",align="center"] -Here you can see the structure and flow from the link:guide-service-layer.asciidoc[service-layer] (REST service call) via the link:guide-logic-layer.asciidoc[logic-layer] to the link:guide-dataaccess-layer.asciidoc[dataaccess-layer] (and back). \ No newline at end of file +In this scheme, you can see the structure and flow from the link:guide-service-layer.asciidoc[service-layer] (REST service call) via the link:guide-logic-layer.asciidoc[logic-layer] to the link:guide-dataaccess-layer.asciidoc[dataaccess-layer] (and back). \ No newline at end of file diff --git a/documentation/guide-dataaccess-layer.asciidoc b/documentation/guide-dataaccess-layer.asciidoc index 20a1af7b..fe8de8d4 100644 --- a/documentation/guide-dataaccess-layer.asciidoc +++ b/documentation/guide-dataaccess-layer.asciidoc @@ -9,4 +9,4 @@ The data-access layer is responsible for all outgoing connections to access and You need to make your choice for a database. Options are documented https://github.com/devonfw/devonfw-guide/blob/master/general/db/guide-database.asciidoc[here]. -The classical approach is to use a Relational Database Management System (RDMS). In such case we strongly recommend to follow our link:guide-jpa.asciidoc[JPA Guide]. Some NoSQL databases are supported by https://spring.io/projects/spring-data[spring-data] so you can consider the link:guide-repository.asciidoc[repository guide]. \ No newline at end of file +The classical approach is to use a Relational Database Management System (RDMS). In such a case, we strongly recommend to follow our link:guide-jpa.asciidoc[JPA Guide]. Some NoSQL databases are supported by https://spring.io/projects/spring-data[spring-data] so you can consider the link:guide-repository.asciidoc[repository guide]. \ No newline at end of file diff --git a/documentation/guide-logic-layer.asciidoc b/documentation/guide-logic-layer.asciidoc index 5ceafe91..6ce21c5f 100644 --- a/documentation/guide-logic-layer.asciidoc +++ b/documentation/guide-logic-layer.asciidoc @@ -4,29 +4,29 @@ toc::[] = Logic Layer The logic layer is the heart of the application and contains the main business logic. -According to our link:architecture.asciidoc#business-architecture[business architecture] we divide an application into link:guide-component.asciidoc[components]. -For each component the logic layer defines a link:guide-component-facade.asciidoc[component-facade]. -According to the complexity you can further divide this into individual link:guide-usecase.asciidoc[use-cases]. +According to our link:architecture.asciidoc#business-architecture[business architecture], we divide an application into link:guide-component.asciidoc[components]. +For each component, the logic layer defines a link:guide-component-facade.asciidoc[component-facade]. +According to the complexity, you can further divide this into individual link:guide-usecase.asciidoc[use-cases]. It is very important that you follow the links to understand the concept of component-facade and use-case in order to properly implement your business logic. == Responsibility -The logic layer is responsible for implementation the business logic according to the specified functional demands and requirements. -It therefore creates the actual value of the application. -The following additional aspects are also in its responsibility: +The logic layer is responsible to implement the business logic according to the specified functional demands and requirements. +Therefore, it creates the actual value of the application. +The following additional aspects are also included in its responsibility: * link:guide-validation.asciidoc[validation] * link:guide-access-control.asciidoc#authorization[authorization] * link:guide-transactions.asciidoc[transaction-handling] (in addition to link:guide-service-layer.asciidoc[service layer]). == Security -The logic layer is the heart of the application. It is also responsible for authorization and hence security is important here. Every method exposed in an interface needs to be annotated with an authorization check, stating what role(s) a caller must provide in order to be allowed to make the call. The authorization concept is described link:guide-security.asciidoc#authorization[here]. +The logic layer is the heart of the application. It is also responsible for authorization and hence security is important in this current case. Every method exposed in an interface needs to be annotated with an authorization check, stating what role(s) a caller must provide in order to be allowed to make the call. The authorization concept is described link:guide-security.asciidoc#authorization[here]. === Direct Object References A security threat are https://www.owasp.org/index.php/Top_10_2013-A4-Insecure_Direct_Object_References[Insecure Direct Object References]. This simply gives you two options: -* avoid direct object references at all +* avoid direct object references * ensure that direct object references are secure -Especially when using REST, direct object references via technical IDs are common sense. This implies that you have a proper xref:authorization[authorization] in place. This is especially tricky when your authorization does not only rely on the type of the data and according static permissions but also on the data itself. Vulnerabilities for this threat can easily happen by design flaws and inadvertence. Here is an example from our sample application: +Especially when using REST, direct object references via technical IDs are common sense. This implies that you have a proper xref:authorization[authorization] in place. This is especially tricky when your authorization does not only rely on the type of the data and according to static permissions but also on the data itself. Vulnerabilities for this threat can easily happen by design flaws and inadvertence. Here is an example from our sample application: -We have a generic use-case to manage BLOBs. In the first place it makes sense to write a generic REST service to load and save these BLOBs. However, the permission to read or even update such BLOB depend on the business object hosting the BLOB. Therefore, such a generic REST service would open the door for this OWASP A4 vulnerability. To solve this in a secure way, you need individual services for each hosting business object to manage the linked BLOB and have to check permissions based on the parent business object. In this example the ID of the BLOB would be the direct object reference and the ID of the business object (and a BLOB property indicator) would be the indirect object reference. +We have a generic use-case to manage BLOBs. In the first place, it makes sense to write a generic REST service to load and save these BLOBs. However, the permission to read or even update such BLOB depends on the business object hosting the BLOB. Therefore, such a generic REST service would open the door for this OWASP A4 vulnerability. To solve this in a secure way, you need individual services for each hosting business object to manage the linked BLOB and have to check permissions based on the parent business object. In this example the ID of the BLOB would be the direct object reference and the ID of the business object (and a BLOB property indicator) would be the indirect object reference. diff --git a/documentation/guide-service-layer.asciidoc b/documentation/guide-service-layer.asciidoc index cf463964..0fd78753 100644 --- a/documentation/guide-service-layer.asciidoc +++ b/documentation/guide-service-layer.asciidoc @@ -13,7 +13,7 @@ are used for communication between different companies, vendors, or partners. * *Internal Services* + are used for communication between different applications in the same application landscape of the same vendor. ** *Back-end Services* + -are internal services between Java back-end components typically with different release and deployment cycles (if not Java consider this as external service). +are internal services between Java back-end components typically with different release and deployment cycles (if not Java consider this as an external service). ** *JS-Client Services* + are internal services provided by the Java back-end for JavaScript clients (GUI). ** *Java-Client Services* + @@ -52,15 +52,15 @@ E.g. creation of the same master-data entity has no effect (no error) * *loosely coupled* + Service consumers have minimum knowledge and dependencies on the service provider. * *normalized* + -complete, no redundancy, minimal +Complete, no redundancy, minimal * *coarse-grained* + Service provides rather large operations (save entire entity or set of entities rather than individual attributes) * *atomic* + Process individual entities (for processing large sets of data, use a link:guide-batch-layer.asciidoc[batch] instead of a service) * *simplicity* + -avoid polymorphism, RPC methods with unique name per signature and no overloading, avoid attachments (consider separate download service), etc. +Avoid polymorphism, RPC methods with unique name per signature and no overloading, avoid attachments (consider separate download service), etc. == Security -Your services are the major entry point to your application. Hence security considerations are important here. +Your services are the major entry point to your application. Hence, security considerations are important here. See link:guide-rest.asciidoc#security[REST Security]. \ No newline at end of file diff --git a/documentation/guide-service-versioning.asciidoc b/documentation/guide-service-versioning.asciidoc index 74a4cfab..ba0cdf0b 100644 --- a/documentation/guide-service-versioning.asciidoc +++ b/documentation/guide-service-versioning.asciidoc @@ -6,7 +6,7 @@ toc::[] This guide describes the aspect and details about versioning of link:guide-service-layer.asciidoc[services] == Motivation -Why versioning of services? First of all you should only care about this topic if you really have to. Service versioning is complex and requires effort (time and budget). The best way to avoid this is to be smart in the first place when designing the service API. +Why versioning of services? First of all, you should only care about this topic if you really have to. Service versioning is complex and requires effort (time and budget). The best way to avoid this is to be smart in the first place when designing the service API. Further, if you are creating services where the only consumer is e.g. the web-client that you deploy together with the consumed services then you can change your service without the overhead to create new service versions and keeping old service versions for compatibility. However, if the following indicators are given you typically need to do service versioning: @@ -17,11 +17,11 @@ However, if the following indicators are given you typically need to do service What are incompatible changes? -* Almost any change when link:guide-soap.asciidoc[SOAP] is used (as it changes the WSDL and breaks the contract). Therefore we recommend to use link:guide-rest.asciidoc[REST] instead. Then only the following changes are critical. +* Almost any change when link:guide-soap.asciidoc[SOAP] is used (as it changes the WSDL and breaks the contract). Therefore, we recommend to use link:guide-rest.asciidoc[REST] instead. Then, only the following changes are critical. * A change where existing properties (attributes) have to change their name * A change where existing features (properties, operations, etc.) have to change their semantics (meaning) -Which changes do not cause incompatibilities? +What changes do not cause incompatibilities? * Adding new service operations is entirely uncritical with link:guide-rest.asciidoc[REST]. * Adding new properties is only a problem in the following cases: @@ -48,9 +48,9 @@ The procedure when rolling out incompatible changes is illustrated by the follow +----------------+ ---- -So here we see a simple example where `App3` provides a Service `S` in Version `v1` that is consumed both by `App1` and `App2`. +So, here we see a simple example where `App3` provides a Service `S` in Version `v1` that is consumed both by `App1` and `App2`. -Now for some reason the service `S` has to be changed in an incompatible way to make it proof for future demands. However, upgrading all 3 applications at the same time is not possible here for whatever reason. Therefore service versioning is applied for the changes of `S`. +Now for some reason the service `S` has to be changed in an incompatible way to make it future-proof for demands. However, upgrading all 3 applications at the same time is not possible in this case for whatever reason. Therefore, service versioning is applied for the changes of `S`. [source] ---- @@ -67,7 +67,7 @@ Now for some reason the service `S` has to be changed in an incompatible way to +----------------+ ---- -Now, `App3` has been upgraded and the new release was deployed. A new version `v2` of `S` has been added while still `v1` is kept for compatibility reasons and that version is still used by `App1` and `App2`. +Now, `App3` has been upgraded and the new release was deployed. A new version `v2` of `S` has been added while `v1` is still kept for compatibility reasons and that version is still used by `App1` and `App2`. [source] ---- @@ -84,7 +84,7 @@ Now, `App3` has been upgraded and the new release was deployed. A new version `v +----------------+ ---- -Now, `App2` has been updated and deployed that is using the new version `v2` of `S`. +Now, `App2` has been updated and deployed and it is using the new version `v2` of `S`. [source] ---- @@ -101,7 +101,7 @@ Now, `App2` has been updated and deployed that is using the new version `v2` of +----------------+ ---- -Now, also `App1` has been updated and deployed that is using the new version `v2` of `S`. The version `v1` of `S` is not used anymore. This can be verified via logging and monitoring. +Now, also `App1` has been updated and deployed and it is using the new version `v2` of `S`. The version `v1` of `S` is not used anymore. This can be verified via logging and monitoring. [source] ---- @@ -118,7 +118,7 @@ Now, also `App1` has been updated and deployed that is using the new version `v2 +----------------+ ---- -Finally version `v1` of the service `S` was removed from `App3` and the new release has been deployed. +Finally, version `v1` of the service `S` was removed from `App3` and the new release has been deployed. == Versioning Schema In general anything can be used to differentiate versions of a service. Possibilities are: @@ -128,15 +128,15 @@ In general anything can be used to differentiate versions of a service. Possibil * Sequential version numbers (e.g. `v1`, `v2`, `v3`) * Composed version numbers (e.g. `1.0.48-pre-alpha-3-20171231-235959-Strawberry`) -As we are following the KISS principle (see link:architecture.asciidoc#key-principles[key principles]) we propose to use sequential version numbers. These are short, clear, and easy while still allowing to see what version is after another one. Especially composed version numbers (even `1.1` vs. `2.0`) lead to decisions and discussions that easily waste more time than adding value. It is still very easy to maintain an excel sheet or release-notes document that is explaining the changes for each version (`v1`, `v2`, `v3`) of a particular service. +As we are following the KISS principle (see link:architecture.asciidoc#key-principles[key principles]) we propose to use sequential version numbers. These are short, clear, and easy while still allowing to see what version is after another one. Especially composed version numbers (even `1.1` vs. `2.0`) lead to decisions and discussions that easily waste more time than adding value. It is still very easy to maintain an Excel sheet or release-notes document that is explaining the changes for each version (`v1`, `v2`, `v3`) of a particular service. We suggest to always add the version schema to the service URL to be prepared for service versioning even if service versioning is not (yet) actively used. For simplicity it is explicitly stated that you may even do incompatible changes to the current version (typically `v1`) of your service if you can update the according consumers within the same deployment. == Practice -So assuming you know that you have to do service versioning the question is how to do it practically in the code. -The approach is as following for your devon4j project in case of code-first: +So assuming you know that you have to do service versioning, the question is how to do it practically in the code. +The approach for your devon4j project in case of code-first should be as described below: -* Determine which types in the code need to be changed. That is for sure the API and implementation of the according service but most likely also impacts transfer objects and potentially even datatypes. +* Determine which types in the code need to be changed. It is likely to be the API and implementation of the according service but it may also impact transfer objects and potentially even datatypes. * Create new packages for all these concerned types containing the current version number (e.g. `v1`). * Copy all these types to that new packages. * Rename these copies so they carry the version number as suffix (e.g. `V1`). @@ -144,15 +144,15 @@ The approach is as following for your devon4j project in case of code-first: * Now you have two versions of the same service (e.g. `v1` and `v2`) but so far they behave exactly the same. * You start with your actual changes and modify the original files that have been copied before. * You will also ensure the links (import statements) of the copied types point to the copies with the version number -* This will cause incompatibilities (and compile errors) in the copied service. Therefore you need to fix that service implementation to map from the old API to the new API and behavior. In some cases this may be easy (e.g. mapping `x.y.z.v1.FooTo` to `x.y.z.FooTo` using link:guide-beanmapping.asciidoc[bean-mapping] with some custom mapping for the incompatible change), in other cases this can get very complex. Be aware of this complexity from the start before you make your decision about service versioning. -* As far as possible this mapping should be done in the service-layer, to not pollute your business code in the core-layer with versioning-aspects. If there is no way to handle it in the service layer, e.g. you need some data from the persistence-layer, implement the "mapping" in the core-layer then, but don't forget to remove this code, when removing the old service version. -* Finally ensure that both the old service behaves as before as well as the new service works as planned. +* This will cause incompatibilities (and compile errors) in the copied service. Therefore, you need to fix that service implementation to map from the old API to the new API and behavior. In some cases, this may be easy (e.g. mapping `x.y.z.v1.FooTo` to `x.y.z.FooTo` using link:guide-beanmapping.asciidoc[bean-mapping] with some custom mapping for the incompatible changes), in other cases this can get very complex. Be aware of this complexity from the start before you make your decision about service versioning. +* As far as possible this mapping should be done in the service-layer, not to pollute your business code in the core-layer with versioning-aspects. If there is no way to handle it in the service layer, e.g. you need some data from the persistence-layer, implement the "mapping" in the core-layer then, but don't forget to remove this code, when removing the old service version. +* Finally, ensure that both the old service behaves as before as well as the new service works as planned. === Modularization -For modularization we also follow the KISS principle (see link:architecture.asciidoc#key-principles[key principles]): -We suggest to have one `api` module per application that will contain the most recent version of your service and gets released with every release-version of the application. The compatibility code with the versioned packages will be added to the `core` module and is therefore not exposed via the `api` module (because it has already been exposed in the previous release of the app). This way you can always for sure determine which version of a service is used by another application just by its maven dependencies. +For modularization, we also follow the KISS principle (see link:architecture.asciidoc#key-principles[key principles]): +we suggest to have one `api` module per application that will contain the most recent version of your service and get released with every release-version of the application. The compatibility code with the versioned packages will be added to the `core` module and therefore is not exposed via the `api` module (because it has already been exposed in the previous release of the app). This way, you can always determine for sure which version of a service is used by another application just by its maven dependencies. -The KISS approach with only a single module that may contain multiple services (e.g. one for each business component) will cause problems when you want to have mixed usages of service versions: You can not use an old version of one service and a new version of another service from the same APP as then you would need to have its API module twice as dependency with different versions what is not possible. However, to avoid complicated overhead for exotic problems we still suggest to follow this easy approach. Only if you come to the point that you really need this complexity you can still solve it (even afterwards by publishing another maven artefact). As we are all on our way to build more but smaller applications (SOA, microservices, etc.) we should always start simple and only add complexity when really needed. +The KISS approach with only a single module that may contain multiple services (e.g. one for each business component) will cause problems when you want to have mixed usages of service versions: You can not use an old version of one service and a new version of another service from the same APP as then you would need to have its API module twice as a dependency on different versions, which is not possible. However, to avoid complicated overhead we always suggest to follow this easy approach. Only if you come to the point that you really need this complexity you can still solve it (even afterwards by publishing another maven artefact). As we are all on our way to build more but smaller applications (SOA, microservices, etc.) we should always start simple and only add complexity when really needed. The following example gives an idea of the structure: diff --git a/documentation/guide-usecase.asciidoc b/documentation/guide-usecase.asciidoc index 45ece640..fad76a0f 100644 --- a/documentation/guide-usecase.asciidoc +++ b/documentation/guide-usecase.asciidoc @@ -4,7 +4,7 @@ toc::[] = UseCase A use-case is a small unit of the link:guide-logic-layer.asciidoc[logic layer] responsible for an operation on a particular link:guide-jpa.asciidoc#entity[entity] (business object). It is defined by an interface (API) with its according implementation. -Following our link:coding-conventions.asciidoc#architecture-mapping[architecture-mapping] use-cases are named `Uc«Operation»«BusinessObject»[Impl]`. The prefix `Uc` stands for use-case and allows to easily find and identify them in your IDE. The `«Operation»` stands for a verb that is operated on the entity identified by `«BusinessObject»`. +Following our link:coding-conventions.asciidoc#architecture-mapping[architecture-mapping], use-cases are named `Uc«Operation»«BusinessObject»[Impl]`. The prefix `Uc` stands for use-case and allows to easily find and identify them in your IDE. The `«Operation»` stands for a verb that is operated on the entity identified by `«BusinessObject»`. For https://en.wikipedia.org/wiki/Create,_read,_update_and_delete[CRUD] we use the standard operations `Find` and `Manage` that can be generated by https://github.com/devonfw/cobigen[CobiGen]. This also separates read and write operations (e.g. if you want to do CQSR, or to configure read-only transactions for read operations). == Find @@ -41,7 +41,7 @@ public interface UcManageBooking { == Custom Any other non CRUD operation `Uc«Operation»«BusinessObject»` uses any other custom verb for `«Operation»`. -Typically such custom use-cases only define a single method. +Typically, such custom use-cases only define a single method. Here is an example: [source,java] ---- @@ -53,7 +53,9 @@ public interface UcApproveBooking { ---- == Implementation -For the implementation of a use-case the same rules apply that are described for the link:guide-component-facade.asciidoc#implementation[component-facade implementation]. +For the implementation of a use-cas, the same rules that are described for the link:guide-component-facade.asciidoc#implementation[component-facade implementation]. + + . However, when following the use-case approach, your component facade simply changes to: @@ -112,14 +114,14 @@ This approach is also illustrated by the following UML diagram: image::images/component-facade-with-use-cases.png["Component facade with use cases.",scaledwidth="80%",align="center"] == Internal use case -Sometimes a component with multiple related entities and many use-cases needs to reuse business logic internally. -Of course this can be exposed as official use-case API but this will imply using transfer-objects (ETOs) instead of entities. In some cases this is undesired e.g. for better performance to prevent unnecessary mapping of entire collections of entities. -In the first place you should try to use abstract base implementations providing reusable methods the actual use-case implementations can inherit from. -If your business logic is even more complex and you have multiple aspects of business logic to share and reuse but also run into multi-inheritance issues, you may also just create use-cases that have their interface located in the `impl` scope package right next to the implementation (or you may just skip the interface). In such case you may define methods that directly take or return entity objects. -To avoid confusion with regular use-cases we recommend to add the `Internal` suffix to the type name leading to `Uc«Operation»«BusinessObject»Internal[Impl]`. +Sometimes, a component with multiple related entities and many use-cases needs to reuse business logic internally. +Of course, this can be exposed as an official use-case API but this will imply using transfer-objects (ETOs) instead of entities. In some cases, this is undesired e.g. for better performance to prevent unnecessary mapping of entire collections of entities. +In the first place, you should try to use abstract base implementations providing reusable methods the actual use-case implementations can inherit from. +If your business logic is even more complex and you have multiple aspects of business logic to share and reuse but also run into multi-inheritance issues, you may also just create use-cases that have their interface located in the `impl` scope package right next to the implementation (or you may just skip the interface). In such a case, you may define methods that directly take or return entity objects. +To avoid confusion with regular use-cases, we recommend to add the `Internal` suffix to the type name leading to `Uc«Operation»«BusinessObject»Internal[Impl]`. == Injection issues -Technically now you have two implementations of your use-case: +Technically, now you have two implementations of your use-case: * the direct implementation of the use-case (`Uc*Impl`) * the component facade implementation (`«Component»Impl`) @@ -127,13 +129,13 @@ Technically now you have two implementations of your use-case: When injecting a use-case interface this could cause ambiguities. This is addressed as following: -* In the component facade implementation (`«Component»Impl`) spring is smart enough to resolve the ambiguity as it assumes that a spring bean never wants to inject itself (can already be access via `this`). -Therefore only the proper use-case implementation remains as candidate and injection works as expected. -* In all other places simply always inject the component facade interface instead of the use-case. +* In the component facade implementation (`«Component»Impl`) spring is smart enough to resolve the ambiguity as it assumes that a spring bean never wants to inject itself (it can already be an access via `this`). +Therefore, only the proper use-case implementation remains as a candidate and injection works as expected. +* In all other places, simply always inject the component facade interface instead of the use-case. In case you might have the lucky occasion to hit this nice exception: ``` org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'uc...Impl': Bean with name 'uc...Impl' has been injected into other beans [...Impl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example. ``` -To get rid of such error you need to annotate your according implementation also with `@Lazy` in addition to `@Named`. \ No newline at end of file +To get rid of such an error you need to annotate your according implementation also with `@Lazy` in addition to `@Named`. \ No newline at end of file