#!/usr/bin/env python # coding: utf-8 # # Introduction # # Software architects have to make sure that the communicated software architecture blueprints exist in the real world. For this, manual inspections as well as automated measurements are needed to avoid surprises. # # In this notebook, I want to show how software architects can define and check for architectural governance rules for Java applications using [jQAssistant](https://jqassistant.org/) and [Neo4j](https://neo4j.com/). # # Idea # This notebook shows an approach to check for architectural rules using the structural code analyzer jQAssistant in combination with the graph database Neo4j. jQAssistant scans several artifacts (Java Bytecode, Git history, Maven Dependencies, and so on) and stores the structural information into the Neo4j database. # # # #
Figure 1: Overview of the analysis pipeline
# # Setup # If you want to execute the notebook, you also need to build the example project and run a Neo4j instance in the background. You can find the instructions [here](https://github.com/JavaOnAutobahn/spring-petclinic/blob/master/readme.md). # # Load extension for querying the Neo4j graph database via [Cypher](https://neo4j.com/developer/cypher-query-language/). # In[1]: get_ipython().run_line_magic('load_ext', 'cypher') # # Define Concerns # In the application at hand (a fork of the Spring PetClinic project), there are Java classes called Entitys, that represent the database table. Each of these classes is marked by a special interface `@Entity`. We can find these classes by querying the graph database and create a higher level concern (or concept) to make further searches easier. # # The following query defines Entitys and returns all found Entitys in the scanned codebase. # # In[2]: get_ipython().run_cell_magic('cypher', '', 'MATCH\n (t:Type)-[:ANNOTATED_BY]->()-[:OF_TYPE]->(a:Type)\nWHERE\n a.fqn="javax.persistence.Entity"\nSET\n t:Entity\nRETURN\n t.fqn AS Entity\n') # # Rule Definition # # Because we want to enable developers to quickly identify Entities in the application, we use the Java package names to define were all the Entities are placed. Thus, Entities must be placed in a package named `model`. # # The following query lists all existing Entities in the scanned codebase: # # In[3]: get_ipython().run_cell_magic('cypher', '', 'MATCH (e:Entity)<-[:CONTAINS]-(p:Package)\nWHERE p.name = "model"\nRETURN e.fqn as Entity, p.name as Package\n') # # Rule Violations # # The following query lists all Enties that doesn't comply to the rule above: # In[4]: get_ipython().run_cell_magic('cypher', '', 'MATCH (e:Entity)<-[:CONTAINS]-(p:Package)\nWHERE p.name <> "model"\nRETURN e.fqn as MisplacedEntity, p.name as WrongPackage\n')