Generic Domain Model

Wolfgang Schuetzelhofer edited this page Jul 31, 2017 · 6 revisions
Previous Next Table of Contents

Generic Domain Model

The domain model, i.e. the model of a domain graph (graph of Java objects) which is stored to / retrieved from a Neo4j graph database by means of JCypher 'Domain Mapping' or JCypher 'Domain Queries' respectively, is specified by those object's classes.
If you don't have access to these Java classes, a generic- (or maybe better a dynamic-) domain model comes in handy. Here are some scenarios, which would highly profit from such a generic domain model:

  • Tools for accessing domain graphs usually want to explore a domain graph or a domain model in a reflective kind of way. They want to be able to explore without having access to the domain model's classes.
  • If you have access to a graph database with a stored domain graph, but you don't have access to the domain model classes, you still want to be able to work with the domain graph or the domain model respectively.
  • If you want to open JCypher for access from other languages and / or for remote access e.g. via a REST-API.

In order to work with generic domain models, JCypher provides a 'sibling interface' to IDomainAccess named IGenericDomainAccess. Instances of this interface can be created by using additional methods in DomainAccessFactory.

IDBAccess dbAccess = // create an IDBAccess

// a domain name is required and must be unique within a database
String domainName = "PEOPLE-DOMAIN";

// create an IDomainAccess
IGenericDomainAccess domainAccess =
   DomainAccessFactory.createGenericDomainAccess(dbAccess, domainName);

IGenericDomainAccess provides the same functionality as IDomainAcces but works on the 'generic domain model'. The code snippet below demonstrates how to retrieve domain objects by type.

// You need to specify the fully qualified type name
// Note: You cannot specify the type directly.
// (Normally you don't have the domain object classes on your classpath
// when working with a generic domain model).
// A list of 'DomainObject' instances is returned.
// 'DomainObject' represents an object of the generic model.
List<DomainObject> domainObjects =
   genricDomainAccess.loadByType("genericdomain.people.model.Person", -1, 0, -1);

DomainObject represents an object of the generic model. We will have a closer look at the classes which make up the generic domain model later on.

You also can perform domain queries which return a generic model. The queries are formulated in the same way you have already seen in the previous chapters about 'Domain Queries'.

// Instantiate an IGenericDomainAccess
IGenericDomainAccess genricDomainAccess =
   DomainAccessFactory.createGenericDomainAccess(dbAccess, domainName);
		
// create a DomainQuery object
GDomainQuery q = genricDomainAccess.createQuery();
		
// create a DomainObjectMatch for objects of type Person
DomainObjectMatch<DomainObject> j_smithMatch =
   q.createMatch("genericdomain.people.model.Person");

// Constrain the set of Persons to contain
// 'John Smith' only
q.WHERE(j_smithMatch.atttribute("lastName")).EQUALS("Smith");
q.WHERE(j_smithMatch.atttribute("firstName")).EQUALS("John");
		
// Traverse forward, start with 'John Smith'
// (j_smithMatch is constraint to match 'John Smith' only),
// navigate attribute 'pointsOfContact',
// end matching objects of type Address.
DomainObjectMatch<DomainObject> j_smith_AddressesMatch =
   q.TRAVERSE_FROM(j_smithMatch).FORTH("pointsOfContact")
      .TO_GENERIC("genericdomain.people.model.Address");
		
// execute the query
DomainQueryResult result = q.execute();

// retrieve the list of matching domain objects
// (i.e. all addresses of 'John Smith')
List<DomainObject> j_smith_Addresses = result.resultOf(j_smith_AddressesMatch);

One difference: A traversal expression ends in TO_GENERIC(..) instead of TO(..). This is because you specify the resulting type name instead of the type itself (remember that you normally don't have the domain object classes on your classpath when working with a generic model).

Classes of the Generic Domain Model

DomainObject represents objects of the generic domain model. It provides methods to access the fields of the domain object like:

/**
* Get a field (attribute) value
* @param fieldName
* @return
*/
public Object getFieldValue(String fieldName)

/**
* Set a field (attribute) value
* @param fieldName
* @param value
*/
public void setFieldValue(String fieldName, Object value)

/**
* if the field is a list or array, answer the value at the given index.
* @param fieldName
* @param index
* @return
*/
public Object getListFieldValue(String fieldName, int index)

/**
* Add a value to a list or array field
* @param fieldName
* @param value
*/
public void addListFieldValue(String fieldName, Object value)

to mention only a few. You will find a couple more accessor methods (you can look at the JavaDoc, the source code, or if using a Java IDE you usually will see all the additional methods).

You can ask a DomainObject for it's type (like you can ask a normal Java Object for it's class). Given you have a DomainObject 'j_smith' which you have retrieved by a query:

// ask the type of a generic domain object
DOType doType = j_smith.getDomainObjectType();

You can ask the domain object type (DOType) which kind of type it is.

// ask which kind of type the given type is
Kind kind = doType.getKind();

// Kind is an enum
public enum Kind {
 CLASS, ABSTRACT_CLASS, INTERFACE, ENUM
}

You can ask the domain object type for it's super type and for the interfaces it implements.

// ask the type for it's super type
DOType sType = doType.getSuperType();

// ask the type for interfaces it implements
List<DOType> interfaces = doType.getInterfaces();

You can ask the domain object type for it's fields or simply for the names of it's fields.

// ask the type for it's declared fields.
List<DOField> declaredFields = doType.getDeclaredFields();
		
// ask the type for all it's fields (including those declared by super types).
List<DOField> allFields = doType.getFields();

// ask the type for the names of it's declared fields.
List<String> declaredFieldNames = doType.getDeclaredFieldNames();
				
// ask the type for all it's fields names
// (including those fields declared by super types).
List<String> allFieldNames = doType.getFieldNames();

You can retrieve a couple more informations from a domain object type (DOType). If you have asked a domain object type for it's fields, you can retrieve field specific information from those DOField objects.

Dynamically Construct your own Domain Model

You not only can explore or modify a domain object (get / set field values) and explore it's structure by means of DOType and DOField, but you also can dynamically construct your own generic domain model. You start with retrieving a DOTypeBuilderFactory.

// Instantiate an IGenericDomainAccess
IGenericDomainAccess genricDomainAccess =
   DomainAccessFactory.createGenericDomainAccess(dbAccess, domainName);
		
// create a type builder factory
DOTypeBuilderFactory tpf = genricDomainAccess.getTypeBuilderFactory();

Then you can ask the DOTypeBuilderFactory to create one of three available builders, depending on what kind of type you are going to build: A DOClassBuilder (for constructing concrete and abstract classes), a DOInterfaceBuilder (for constructing interfaces), or a DOEnumBuilder (for constructing enums). If you are finished specifying a type by means of a builder you simply call build() on that builder which will answer the appropriate domain object type (DOType).
The following code snippet shows how to dynamically construct a domain model which is very similar to the PEOPLE-DOMAIN model (or to a subset of that model) used in the JCypher Samples Project.

// Construct the domain model

// Construct the enum SubjectTypes
DOEnumBuilder subjectTypesBuilder =
   tpf.createEnumBuilder("mydomain.model.SubjectTypes");
subjectTypesBuilder.addEnumValue("NAT_PERSON");
subjectTypesBuilder.addEnumValue("JUR_PERSON");
DOType subjectTypes = subjectTypesBuilder.build();
		
// Construct the interface PointOfContact
DOInterfaceBuilder pointOfContactBuilder =
   tpf.createInterfaceBuilder("mydomain.model.PointOfContact");
DOType pointOfContact = pointOfContactBuilder.build();
	
// Construct the class Address which implements PointOfContact	
DOClassBuilder addressBuilder =
   tpf.createClassBuilder("mydomain.model.Address");
addressBuilder.addInterface(pointOfContact);
addressBuilder.addField("street", String.class.getName());
addressBuilder.addField("number", int.class.getName());
DOType addressType = addressBuilder.build();
		
// Construct the abstract class Subject
DOClassBuilder subjectTypeBuilder =
   tpf.createClassBuilder("mydomain.model.Subject");
subjectTypeBuilder.setAbstract();
subjectTypeBuilder.addField("subjectType", subjectTypes.getName());
subjectTypeBuilder.addListField("pointsOfContact",
   "mydomain.model.PointOfContact");
DOType subject = subjectTypeBuilder.build();
		
// Construct the class Person as a subclass of Subject
DOClassBuilder personTypeBuilder =
   tpf.createClassBuilder("mydomain.model.Person");
personTypeBuilder.addField("firstName", String.class.getName());
personTypeBuilder.addField("lastName", String.class.getName());
personTypeBuilder.addField("birthDate", Date.class.getName());
personTypeBuilder.setSuperType(subject);
DOType personType = personTypeBuilder.build();

Retrieve types from a loaded Generic Domain Model

Alternatively you can retrieve existing types from a generic domain model which was loaded from a graph database.

// Instantiate an IGenericDomainAccess
IGenericDomainAccess genricDomainAccess =
   DomainAccessFactory.createGenericDomainAccess(dbAccess, domainName);
		
// retrieve an existing type
DOType doType =
   genricDomainAccess.getDomainObjectType("genericdomain.people.model.Person");

If you don't know a valid type name, you can consult the DomainInformation.

// Create a DomainInformation object for a certain domain.
DomainInformation di = DomainInformation.forDomain(dbAccess, domainName);
				
// Answer a list of names of DomainObjectTypes stored in the domain
List<String> typeNames = di.getDomainObjectTypeNames();

Instantiate a Generic Domain Object

Having either retrieved or constructed generic domain object types (DOType), you can instantiate one or more generic domain objects (DomainObject) and fill their fields appropriately.

DOType addressType = ...;
DOType personType = ...;

// that's an enum, see above
DOType subjectTypes = ...;

// instantiate an address and set field values
DomainObject anAddress = new DomainObject(addressType);
anAddress.setFieldValue("street", "Market Street");
anAddress.setFieldValue("number", 42);
		
// instantiate a person and set field values
DomainObject aPerson = new DomainObject(personType);
aPerson.setFieldValue("firstName", "Maxwell");
aPerson.setFieldValue("lastName", "Smart");
GregorianCalendar cal = new GregorianCalendar(1940, 0, 22);
Date birthDate = cal.getTime();
aPerson.setFieldValue("birthDate", birthDate);
aPerson.setFieldValue("subjectType", subjectTypes.getEnumValue("NAT_PERSON"));
aPerson.addListFieldValue("pointsOfContact", anAddress);

You can then use IGenericDomainAccess to store generic domain objects to the graph database.

// Instantiate a generic domain object
// (see above)
DomainObject aPerson = ...;

// Instantiate an IGenericDomainAccess
IGenericDomainAccess genricDomainAccess =
   DomainAccessFactory.createGenericDomainAccess(dbAccess, domainName);

// now store the newly created person
List<JcError> errors = genricDomainAccess.store(aPerson);
if (errors.size() > 0) {
   // do something in case of errors
}

Previous Next Table of Contents