Do you still code everything by hand? Isn't it tedious and error prone? It's time to start using Sculptor to jump start Model Driven Software Development. Concepts and patterns from Domain-Driven Design are used in the Domain Specific Language, which is the model for the generated Hibernate and Spring implementation. Have you had frustrating experiences with code generators? Have you become disillusioned with code generators? Was the generated code not entirely satisfactory, and you couldn't control the result? Sculptor is different!
Sculptor is a simple and powerful code generation platform, which provides a quick start to Model Driven Software Development (MDSD). When using Sculptor you can focus on the business domain, instead of technical details. You can use the concepts from Domain-Driven Design (DDD) in the textual Domain Specific Language (DSL). Sculptor uses openArchitectureWare (oAW) to parse the DSL and generate high quality Java code and configuration. The generated code is based on well-known frameworks, such as Spring, Hibernate and Java EE.
![]() |
Figure 1. Overview of Sculptor
Figure 1, “Overview” illustrates how the developer specifies the application design in the DSL, generates code and configuration with Maven 2. Generated code and hand written code is well separated. Hand written code, such as JUnit tests and business logic, is added in subclasses or other well defined places.
The DSL and the code generation drives the development and is not a one time shot. It is an iterative process, which can be combined with Test Driven Development (TDD) and evolutionary design, as explained in the appendix Test Driven Development with Sculptor.
High level features of Sculptor:
To illustrate how Sculptor can be used in practice we will use an example. It is an introduction and not a complete User's Guide, more information can be found at the Sculptor site.
The example is a simple system for a library of movies and books. The core of the system is a Domain Model, see Figure 2, “Domain model” A Library consists of PhysicalMedia. Books and Movies are different types of Media, which are stored on a PhysicalMedia, e.g. DVD, VHS, paper books, eBooks on CD. A Media has Characters, e.g. James Bond, which can be played by a Person, e.g. Pierce Brosnan. A person can be involved (Engagement) in different Media, actually a Person can have several Engagements in the same Media. E.g. Quentin Tarantino is both actor and director in the movie 'Reservoir Dogs'.
Figure 2. Domain model of the Library example.
With a few simple Maven commands you will be able to create the Maven and Eclipse projects for your application. Sculptor provides Maven Archetype artifacts to facilitate this.
A Sculptor application is defined in a textual DSL. Since it is text it has all the benefits of ordinary text source code, such as searching, copy-paste, merging and so on. Sculptor provides an Eclipse editor for the DSL. It supports error highlight, code completion and outline view.
Sculptor doesn't mandate any specific development methodology, but personally I prefer Test Driven Development with evolutionary design and refactoring. Using that approach the DSL model is not a big design up front. This is described in more detail in the appendix Test Driven Development with Sculptor.
An early DSL model for the Library example might look like this:
Application Library { basePackage = org.library Module movie { Service LibraryService { findLibraryByName delegates to LibraryRepository.findLibraryByName; saveLibrary delegates to LibraryRepository.save; findMovieByName delegates to MovieRepository.findByName; } Entity Library { String name key reference Set<@Movie> movies Repository LibraryRepository { save; @Library findLibraryByName(String name) throws LibraryNotFoundException; protected findByCriteria; } } Entity Movie { String title not changeable String urlIMDB key Integer playLength Repository MovieRepository { List<@Movie> findByName(Long libraryId, String name) delegates to FindMovieByNameAccess; } } } }
The full DSL model for the Library example looks like this. There you can see that the DSL defines two Modules, containing one Service each. It defines the same Domain Objects, including attributes and references, as in Figure 2, “Domain model”.
The core concepts of the DSL have been taken from Domain-Driven Design. If you don't have the book you can download and read more in DDD Quickly.
Sculptor code generation is executed as part of the Maven 2 build cycle, i.e. you run mvn install
as usual. When developing it is also convenient to use mvn generate-sources
, which will only generate, without performing compile, test and package.
The artifacts that are intended to be completed with hand written code are generated only once, while other artifacts are regenerated and overwritten each time. The regenerated source should not be added to version control. These two types of artifacts are separated from each other in the file system. The source and resources directories can be seen in Figure 3, “File structure”. It also shows the packages and generated classes in the domain package for the media module. The names of the packages can of course be changed easily.
Figure 3. File structure and packages
One of the strengths of Sculptor, compared to other code generators, is that it spans the complete application, not only fragments, which are hard to fit in to the overall design. Figure 4, “Generated artifacts” lists the most important artifacts that are generated by Sculptor 1.0. You can extend Sculptor to span other areas and the Sculptor development team has several ideas for future expansion, e.g. client web application.
Sculptor typically reduce the amount of hand written code with 50% compared to a manually coded application with a similar design. Appendix: Sculptor Metrics presents measurements from a real case.
Figure 4. Overview of generated artifacts
In the context of Sculptor, Domain Object is a common term for Entity, ValueObject and BasicType.
Entities have an identity and the state of the object may change during the lifecycle. For Value Objects the values of the attribues are interesting, and not which object it is. Value Objects are typically immutable. BasicType is used for fundamental types, such as Money. BasicType is a ValueObject which is stored in the same table as the Domain Object referencing it. It corresponds to Hibernate Component.
Domain Objects are implemented as ordinary POJOs. They can have simple attributes and references to other Domain Objects. They can of course also contain behavior; otherwise it wouldn't be a rich domain model. However, the behavior logic is always written manually and not defined in the DSL.
It is possible to specify that a ValueObject is not persistent, i.e. not stored in database. This is for example useful for parameters and return values for service operations.
Below is the definition in the DSL of a few Domain Objects in the Library example. Figure 5, “Domain Object” illustrates the corresponding artifacts that are generated.
Entity Person { String ssn key length="20" String country key length="2" Integer age reference @PersonName name } BasicType PersonName { String first String last } ValueObject MediaCharacter { String name not changeable reference Set<@Person> playedBy reference Set<@Media> existsInMedia opposite characters }
Figure 5. Generated Domain Object artifacts
Separation of generated and manually written code is done by a generated base class and manually written subclass. See Figure 5, “Domain Object”. It is in the subclass you add methods to implement the behavior of the Domain Object. The subclass is also generated, but only once, it will never be overwritten by the generator. You can of course remove it to regenerate it.
equals
and hashCode
requires some thought when used with Hibernate, see the discussion at the Hibernate site. Sculptor takes care of the details and this is a typical example of how Sculptor raises the level of abstraction by distilling best practice. You only have to mark the attributes that is the natural key of the Domain Object, or if there is no natural key Sculptor will generate a UUID automatically.
The Sculptor DSL is based on the principle "convention over configuration". An example of this is that Entities are by default auditable, which means that when the objects are saved an interceptor will automatically update information about by whom and when the object was created or changed These attributes are automatically added for auditable Domain Objects. You can turn off auditing for an Entity with not auditable
. Value Objects are by default not auditable.
The Services act as a Service Layer around the domain model. It provides a well defined interface with a set of available operations to the clients.
Services are implemented as illustrated in Figure 6, “Service”. EJB 3 stateless session bean is the default implementation, but EJB 2.1 deployment is also supported.
The transaction boundary is at the service layer. Hibernate session demarcation and error handling are implemented with Spring AOP in front of the Services.
In the DSL an operation of a Service almost looks like an ordinary Java method with return type, parameters and throws declaration.
Service LibraryService { inject LibraryRepository inject MediaRepository @Library findLibraryByName(String name) throws LibraryNotFoundException; saveLibrary delegates to LibraryRepository.save; findMediaByName delegates to MediaRepository.findMediaByName; List<@Media> findMediaByCharacter(Long libraryId, String characterName); findPersonByName delegates to PersonService.findPersonByName; }
Figure 6. Generated Service artifacts
In the Service implementation hand written code can be added for the behavior of the Service. You can also easily delegate to an operation in a Repository or another Service. Then you only have to declare the name of the operation in the DSL. Return type and parameters are "copied" from the delegate operation.
Sculptor can also generate message consumers, implemented as EJB Message Driven Beans. These features are not illustrated in this example.
The Repositories encapsulate all technical details for retrieving Domain Objects from the database. They are also used to make new objects persistent and to delete objects.
The interface of the Repository speaks the Ubiquitous Language of the domain. It provides domain centric data access of the Aggregate roots of the domain model.
Repository MediaRepository { int getNumberOfMovies(Long libraryId) delegates to AccessObject; save; findByQuery; List<@Media> findMediaByName(Long libraryId, String name); }
Figure 7. Generated Repository artifacts
The default implementation of a Repository consists of an implementation class and Access Objects. See Figure 7, “Repository”. The intention is a separation of concerns between the domain and the data layer. Repository is close to the business domain and Access Objects are close to the data layer. The Hibernate specific code is located in the Access Object, and not in the Repository.
Sculptor runtime framework provides generic Access Objects for operations such as findByCriteria, findByQuery, findByKeys, save and delete. It is important that the API of the Repository communicates supported operations for the Aggregate root and therefore these generic operations are not added automatically to the Repository. They must be specified in the DSL, but that is very simple.
This section is intended as a teaser of how Sculptor can be customized. More information is available in Sculptor Developer's Guide.
Sculptor is not a one-size-fits-all product. Even though it is a good start for many systems, sooner or later customization is always needed. Some things can easily be changed with properties or AOP, while other things require more effort. However, Sculptor doesn't pretend that it can solve all problems out-of-the-box, but is designed and documented so that you as a developer can take full control of the tool without too much effort.
Sculptor is implemented with openArchitectureWare (oAW) and Eclipse Modeling Framework (EMF).
Figure 8. Internal Design of Sculptor
model.design
, i.e. the source for the concrete model that is the input to the code generation process. Constraints of the DSL are validated while editing.
workflow.oaw
is executed. It doesn't contain much. sculptorworkflow.oaw
which defines the flow of the code generation process. model.design
file using the oAW XText parser. Constraints of the DSL are checked. sculptormetamodel.ecore
meta model. The meta model is defined with EMF Ecore. You can customize rather much with simple configuration properties. Examples:
The actual code generation is done with oAW XPand, which is a powerful and simple to use template language. The templates can be structured very much like methods, with small definitions, which expand other definitions.
You can change the code generation templates using the Aspect-Oriented Programming (AOP) feautures in oAW XPand. You can "override" the definitions in the original templates. For example if you need to replace the UUID generation:
«IMPORT sculptormetamodel» «EXTENSION extensions::helper» «EXTENSION extensions::properties» «AROUND *::uuidAccessor FOR DomainObject» public String getUuid() { // lazy init of UUID if (uuid == null) { uuid = org.myorg.MyUUIDGenerator.generate().toString(); } return uuid; } private void setUuid(String uuid) { this.uuid = uuid; } «ENDAROUND»
It is also possible to use AOP to exclude generation. For some special cases the default generation might not be appropriate and it is desirable to handle the special case with manual code instead. For example, assume we have a complex domain object and we need to do the Hibernate mapping manually.
You are not stuck if you need to do more customization than what is possible with properties and AOP. You can checkout the Sculptor source code to do more adjustments. It is well described in the Sculptor Developer's Guide how to change different things. A few examples of stuff you can modify:
Sculptor is an Open Source tool with the purpose to improve developer productivity and quality. It raises the level of abstraction and automates a lot of otherwise repetitive manual coding.
You can focus on the business domain, instead of technical details. Sculptor distills best practice into easy to use notation. The design of the generated application is heavily inspired by the patterns and concepts from Domain-Driven Design.
You are in control and can easily adopt the tool to fit the requirements and frameworks you are working with.
Patrik Nordwall is a software developer for Avega, an IT consultancy company in Stockholm, Sweden. He has 11 years of experience in Java development and is a skilled software architect. His areas of expertise include JEE, Swing, design patterns, frameworks, and miscellaneous open source such as Spring, Hibernate and Eclipse. Patrik is a Sun certified Java programmer, developer and enterprise architect. You can reach him at [email protected]