You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2012/12/21 15:13:51 UTC
[4/4] ISIS-289: refactoring applib doc
http://git-wip-us.apache.org/repos/asf/isis/blob/208dc100/core/applib/src/docbkx/guide/isis-applib.xml
----------------------------------------------------------------------
diff --git a/core/applib/src/docbkx/guide/isis-applib.xml b/core/applib/src/docbkx/guide/isis-applib.xml
index 45ebd3a..28ccc04 100644
--- a/core/applib/src/docbkx/guide/isis-applib.xml
+++ b/core/applib/src/docbkx/guide/isis-applib.xml
@@ -58,13 +58,8 @@
<para><emphasis>Apache Isis</emphasis> is designed to allow programmers
rapidly develop domain-driven applications following the <ulink
url="http://en.wikipedia.org/wiki/Naked_Objects">Naked Objects</ulink>
- pattern. It is made up of a core framework plus a number of alternate
- implementations, and supports various viewers and runtimes/object stores.
- Apache Isis is hosted at the <ulink
- url="http://incubator.apache.org/isis">Apache Foundation</ulink>, and is
- licensed under <ulink
- url="http://www.apache.org/licenses/LICENSE-2.0.html">Apache Software
- License v2</ulink>.</para>
+ pattern. It is made up of a core plus a number of components for each of
+ the main APIs: objectstores, security, viewers and profilestores. </para>
<para>This guide is written for programmers looking to understand the
programming conventions, annotations and supporting utilities within the
@@ -72,981 +67,16 @@
<emphasis>applib</emphasis>), in order that the framework can correctly
pick up and render the business rules and logic encoded within their
domain objects.</para>
+
+ <para><emphasis>Apache Isis</emphasis> is hosted at the <ulink
+ url="http://incubator.apache.org/isis">Apache Foundation</ulink>, and is
+ licensed under <ulink
+ url="http://www.apache.org/licenses/LICENSE-2.0.html">Apache Software
+ License v2</ulink>.</para>
</preface>
<!-- main content -->
- <chapter>
- <title>Introduction</title>
-
- <abstract>
- <para>What this guide contains, who it is for, and how the applib
- relates to other parts of the framework.</para>
- </abstract>
-
- <sect1>
- <title>What's in this Guide</title>
-
- <para>This guide describes the set of conventions for writing domain
- objects, that are together known as the <emphasis>Apache Isis</emphasis>
- Programming Model. It's important reading for developers who are looking
- to write domain-driven applications either for prototyping or deployment
- on <emphasis>Apache Isis</emphasis>.</para>
-
- <para>More correctly, this guide actually documents the
- <emphasis>default</emphasis> programming model, targetted for the Java
- programming language, and implemented by the
- <classname>org.apache.isis.progmodels:dflt</classname> module.
- <emphasis>Isis</emphasis> is in fact capable of supporting different
- programming models, either for other JVM-based languages; for example
- the Groovy language is supported in the
- <classname>org.apache.isis.progmodels:groovy</classname> module. Those
- other programming models have their own documentation.</para>
- </sect1>
-
- <sect1>
- <title>This Guide vs the Core Documentation</title>
-
- <para>This guide assumes that you have at least the outline of a running
- <emphasis>Apache Isis</emphasis> application, for example having run the
- quickstart archetype. Details of how to run the quickstart archetype can
- be found on the <emphasis>Isis</emphasis> <ulink
- url="http://incubator.apache.org/isis">website</ulink>.</para>
-
- <para>This guide has two main objectives:</para>
-
- <itemizedlist>
- <listitem>
- <para>to describe the general approach for developing Isis
- applications solely by writing domain objects</para>
- </listitem>
-
- <listitem>
- <para>to describe the specifics of writing domain objects according
- to the conventions of the <emphasis>default</emphasis> programming
- model.</para>
- </listitem>
- </itemizedlist>
-
- <para>The core documentation, on the other hand, explains the wider
- landscape of what makes up an <emphasis>Isis</emphasis> application,
- dealing with such matters as running the app with different components,
- eg viewers, security, programming models, profile stores and
- runtimes/object stores. Included within this is an explanation of how
- you can build your <emphasis>own</emphasis> programming model (typically
- as a subset of the default programming model described here, but
- possibly with additional custom elements).</para>
- </sect1>
-
- <sect1>
- <title>Where Next?</title>
-
- <para>This guide is in several parts. The first part is intended to
- explain, from a developers' perspective, what exactly <emphasis>Apache
- Isis</emphasis> is. The second part has chapters that describe the
- specifics in the style of recipes/cookbooks, providing a "how-to" guide
- on developing domain objects. The third part provides details on other
- supporting features relevant to writing domain applications. The final
- part is a series of appendices that provide details of the programming
- model in reference form. You should find this more useful if you've used
- <emphasis>Isis</emphasis> for a while and just need to check on a
- specific detail.</para>
- </sect1>
- </chapter>
-
- <part id="prt.UnderstandingApacheIsis">
- <title>Understanding Apache Isis</title>
-
- <partintro>
- <para>The chapters in this part of the guide are intended to help you
- understand <emphasis>Apache Isis</emphasis> from a developers'
- perspective:</para>
-
- <itemizedlist>
- <listitem>
- <para>the principles and patterns that underpin
- <emphasis>Isis</emphasis> (<xref
- linkend="chp.PrinciplesAndPatterns" />)</para>
- </listitem>
-
- <listitem>
- <para>a development process to follow (<xref
- linkend="chp.DevelopmentProcess" />)</para>
- </listitem>
- </itemizedlist>
- </partintro>
-
- <chapter id="chp.PrinciplesAndPatterns">
- <title>Apache Isis and Naked Objects</title>
-
- <abstract>
- <para>Understanding Apache Isis and the naked objects pattern by way
- of a series of questions and answers.</para>
- </abstract>
-
- <para>The headline feature that distinguishes <emphasis>Apache
- Isis</emphasis> from other frameworks is its support for the naked
- objects pattern - the ability to generate a user interface (at runtime)
- directly from the domain model. However, there's much more to
- <emphasis>Apache Isis</emphasis> than this.</para>
-
- <para>This chapter introduces the <emphasis>Apache Isis</emphasis> and
- the naked objects pattern by way of a series of questions and answers.
- It is adapted from an <ulink
- url="http://www.infoq.com/articles/haywood-ddd-no">interview originally
- published on InfoQ</ulink>.</para>
-
- <sect1>
- <title>Apache Isis implements the naked objects pattern... but what is
- that, exactly?</title>
-
- <para>Naked objects is an architectural pattern where the idea is to
- automatically expose a domain model objects directly within a
- object-oriented user interface... not just their state (properties and
- collections) but also their behaviour (what we call actions). You can
- think of it as analogous to an <acronym>ORM</acronym> such as <ulink
- url="http://hibernate.org">Hibernate</ulink>; but whereas an
- <acronym>ORM</acronym> reflects the domain model into the persistence
- layer, naked objects reflects the domain model into the presentation
- layer.</para>
-
- <para>Naked Objects (capitals) was a Java framework that implemented
- the naked objects (lower case) pattern. Since then, we've taken the
- original framework, along with a number of sister projects developed
- by the community, into the Apache Incubator. So, what was originally
- "Naked Objects" is now Apache Isis.</para>
- </sect1>
-
- <sect1>
- <title>What type of applications are best suited to Isis?</title>
-
- <para>The naked objects pattern requires that your primary
- consideration is in building an object-oriented domain model, so it's
- most suitable for enterprise applications that have complex business
- rules where there's a desire to represented them within such a domain
- model. The idea - at least during the initial stages - is to build up
- a domain model quickly, and to get feedback from the domain experts by
- exploiting the framework's ability to expose that directly domain
- model within the user interface. A picture tells a thousand
- words.</para>
-
- <para>Apache Isis "sweet spot" are those applications that are used
- internally within the organization, by experienced users who are
- comfortable with the entities within the domain model, and just need
- an application that imposes as few constraints as possible on how it
- is used. These are sometimes called <emphasis>sovereign
- applications</emphasis>. If you're a developer then your
- <acronym>IDE</acronym> is probably the sovereign application you use
- the most, and how often did you use any of its wizards? And if you use
- telephone banking then you'll also know how frustrating a sovereign
- application can be that is too invasive in dictating the workflow...
- that poor person on the other end asks making apologies while she goes
- exits from one screen and then goes into another to check some detail
- on a bank account. The generic <acronym>OO</acronym>
- <acronym>UI</acronym>s generated by naked objects impose no such
- restrictions, and so are ideal for this sort of application.</para>
- </sect1>
-
- <sect1>
- <title>And are there applications where the OO UIs generated by Isis
- are less suitable?</title>
-
- <para>The opposite of a sovereign application (above) is sometimes
- called a <emphasis>transient application</emphasis>. These are ones
- used only occasionally or by inexperienced users, typically outside
- the organization (ie your customers), who don't know or care to know
- the domain model, and just want to be led through the system to
- accomplish some well-defined goal. A good example here is a check-in
- kiosk at an airport... you just want to get on the plane, and be given
- the opportunity to choose your seat. But you likely won't care which
- plane, or even plane type, nor who the pilot is; these are unimportant
- details.</para>
-
- <para>For this sort of application a generic <acronym>OO</acronym>
- <acronym>UI</acronym> that exposes lots of the domain is clearly not
- appropriate. Instead, we want an application that exposes view models
- rather than entities, and where the <acronym>UI</acronym> can be
- customized. The view model object is responsible for managing the
- workflow for the user story, exposing just the subset of the domain
- that is relevant, and hiding the rest of the domain. We find that
- these view models can be layered on top of the entities once those
- entities are understood.</para>
-
- <para>Using view model objects is necessary but probably not
- sufficient for transient applications; we also usually need to
- customize the <acronym>UI</acronym>. <emphasis>Apache Isis</emphasis>
- has two viewers that allow the UI to be customized, one that provides
- a set of taglibs, and one that provides a set of <ulink
- url="http://wicket.apache.org">Apache Wicket</ulink>
- components.</para>
-
- <para>Putting both of these techniques together (custom view models
- and views) means that Apache Isis is suitable for transient
- applications as well as sovereign applications. However, the former
- will necessarily take more work as opposed to the latter.</para>
- </sect1>
-
- <sect1>
- <title>What is the typical development process life cycle for
- applications built with Isis, and how does it compare to traditional
- application development process?</title>
-
- <para>The main difference you're likely to encounter is the emphasis
- on developing the domain model at the same time as identifying and
- prioritizing user stories, what those practicing domain driven design
- call a "ubiquitous language" for the team.</para>
-
- <para>So, rather than let the developers in the team "discover" the
- domain model as part of story implementation, we'd expect that the
- domain experts/business analysts have already identified some of the
- main domain concepts through prototyping or spikes, and these can be
- used to help communicate the stories to the developers during the
- planning game. Not every business analyst is going to feel comfortable
- working with an <acronym>IDE</acronym>, but it works well to pair with
- a developer. Because an Apache Isis application can be runs just from
- domain classes, it's possible to develop the app very very quickly.
- We've found a good technique is to run workshops with the business
- analyst facilitating the meeting with the domain experts, and the
- developer acting as "code monkey" to rapidly convert the ideas into an
- app. Obviously the code isn't production quality, but it allows the
- team as a whole to go very quickly and experiment with different
- representations of the model.</para>
-
- <para>Another alternative is to go a little more slowly, and have the
- analyst and developer pairing to write production code. The analyst
- still focuses on identifying and naming the domain concepts and their
- relationships, while the developer's role (as well as learning about
- the domain) is to ensure that there's enough rigour and tests around
- the code that's been written while this is being done. Of course, both
- approaches to pairing can be used, sometimes spiking ideas, sometimes
- going straight to writing tested code.</para>
-
- <para>Of course, while the naked objects pattern means that no
- <acronym>UI</acronym> code needs to be written in order to elicit
- feedback, there do still need to be objects in the app for the domain
- expert to view. Apache Isis has a pluggable runtime/persistence layer,
- and for prototyping and most of development we recommend using the
- in-memory object store. To populate this object store for each run we
- use fixtures, which we usually arrange into a composite pattern to
- setup data needed for the particular scenario we might be working on.
- Using Isis in this way means that we spend our time alternating
- between defining domain classes and writing fixture data. This can be
- a great benefit because it helps the domain experts - who after all
- aren't necessarily technical - distinguish between what are classes
- and what are instances of classes. For example, if I'm writing a
- library system and we identify there are loanable books and reference
- books, we need to know whether "loanable book" and "reference book"
- are different subclasses of a book superclass, or merely different
- instances of a book class with, say, a loanable property set to true
- or false. Seeing a running application makes it much easier for the
- domain expert to make the call.</para>
-
- <para>In terms of writing tests, there are several options. First,
- because the domain objects you write are basically pojos that follow
- some naming conventions, they are very easy to test using standard
- <acronym>TDD</acronym> tools such as <ulink
- url="http://junit.org">JUnit</ulink> and <ulink
- url="http://jmock.org">JMock</ulink>. And if you wanted to, you could
- test the <acronym>UI</acronym> generated by Isis using <ulink
- url="http://seleniumhq.org/">Selenium</ulink> and similar; indeed we'd
- recommend that if you've customized the <acronym>UI</acronym>.</para>
-
- <para>In addition, though, Isis provides a couple of additional
- BDD/testing framework integrations. One of these is an integration
- with <ulink url="http://fitnesse.org">FitNesse</ulink>, and a newer
- one we've worked on is an integration with <ulink
- url="http://concordion.org">Concordion</ulink>. In both cases the
- integrations provide the glue code so you can exercise your domain
- objects directly. For example, your spec might say "the place order
- action cannot be invoked if the product is out of stock". All the
- developer needs to do is to wire the spec to these pre-canned
- integrations, specifying that the
- <methodname>placeOrder()</methodname> action should be called on a
- <classname>Product</classname>.</para>
-
- <para>Isis also provides a JUnit runner that works by bootstrapping
- the Isis runtime for each test, and proxying the domain objects with a
- bit of cglib so that they are interacted with "as if" through the
- <acronym>UI</acronym>. For example, let's go back to that
- <methodname>placeOrder()</methodname> action. To prevent this from
- being called for a <classname>Product</classname> that's out of stock,
- the developer would write a corresponding
- <methodname>validatePlaceOrder()</methodname> method. Isis
- automatically calls this validate method prior to the
- <methodname>placeOrder()</methodname> method, and uses the return
- value ("that product is out of stock") as the reason why the action
- can't be invoked. In the <acronym>UI</acronym>, this business rule is
- represented by greying out the OK button of the action. In the JUnit
- runner, this rule is represented by having the cglib proxy throw a
- validation exception if the test calls
- <methodname>placeOrder()</methodname> with an out of stock
- <classname>Product</classname>.</para>
-
- <para>In terms of deployment, there's several steps involved, probably
- the most significant being to select the runtime and object store. As
- noted above, Isis has a pluggable persistence layer; you can use the
- in-memory object store for developing, but will want to switch to a
- "real" object store for deployment. The amount of work required here
- depends on the object store selected; in the case of
- <acronym>JPA</acronym>/Hibernate support, for example, it amounts to
- annotating your domain entities with annotations, and writing
- implementations of some of the repositories to make the relevant
- <acronym>SQL</acronym> calls.</para>
- </sect1>
-
- <sect1>
- <title>What are the limitations of Apache Isis framework?</title>
-
- <para>It's certainly true that naked objects pattern implemented by
- <emphasis>Apache Isis</emphasis> is opinionated, so if you don't agree
- with all of its opinions then you're going to find it limiting in one
- way or another.</para>
-
- <para>The first of these is that everything that the user wants to
- interact with has to be an object of some sort. As already explained,
- for sovereign applications, these are likely to be the persisted
- domain entities, but for a transient application, the object may be a
- view model to be support a particular workflow, and which may or may
- not be persisted. Such an approach probably wouldn't be considered
- much of a limitation, but it's worth contrasting with architectures
- (eg Spring Web Flow or Struts) where put the responsibility for
- tracking workflow lives not in an object but instead within some sort
- of declarative XML markup (or even just in the interplay between
- controllers and views).</para>
-
- <para>A slightly more subtle consequence of the above is that
- integration of different technologies happens through the domain
- objects, rather than in front of them through application-layer or
- <acronym>UI</acronym> layer mashups. For example, supposing that we
- wanted to have an <acronym>SMS</acronym> sent out to confirm a
- checkout. If you were writing the <acronym>UI</acronym> and
- application service layer yourself, you might choose to have the
- application layer make the call to the <acronym>SMS</acronym> service,
- judging it to be a coordination responsibility. With
- <emphasis>Isis</emphasis>, though, you don't get the chance to write
- any application layer code, and so this would be done by having the
- domain object call out to the <acronym>SMS</acronym> service. The
- <acronym>SMS</acronym> service would be defined by an interface, and
- the implementation would be injected into the domain object by the
- framework. For some reason not everyone is necessarily comfortable
- about injecting domain services into entities; but it works well for
- us and we're sticking with it.</para>
-
- <para>Another area where <emphasis>Isis</emphasis> is going to feel
- different - and perhaps limiting to some - is that it pushes some
- responsibilities onto the domain objects that otherwise might sit in
- other layers. For example, above we discussed that a
- <methodname>placeOrder()</methodname> action on a
- <classname>Customer</classname> can have a supporting
- <methodname>validatePlaceOrder()</methodname> method. It's also
- possible to have <methodname>disablePlaceOrder()</methodname> method,
- which if returning non-null will cause the action to be greyed out the
- action in the <acronym>UI</acronym>. For example, a blacklisted
- <classname>Customer</classname> might not allow any orders to be
- placed, and would indicate this through the disable method. Some might
- consider this as a misplaced presentation concern. However, the
- <classname>Customer</classname> isn't greying out the
- <acronym>UI</acronym> itself; the disable method is merely a
- convention by which the presentation layer can interrogate the
- entity.</para>
-
- <para>Something else we hear sometimes about naked objects-style
- systems is that they are really only suitable for
- <acronym>CRUD</acronym> style applications. It has to be said that
- it's a criticism that does irk; because although its true that naked
- objects frameworks automatically expose object state, they also expose
- object's behaviour. That is, every public method that is not a
- property or a collection is taken to be an action and will rendered by
- the <acronym>UI</acronym> as a button or a link. Indeed, we sometimes
- like to talk of behaviourally complete domain objects; it's the very
- antithesis of the anaemic domain model anti-pattern.</para>
-
- <para>All the above notwithstanding, probably the biggest impediment
- to going out and using Isis right now is that it's a small community
- and as such the codebase is still relatively new. But if that is a
- turn-off, note that you can still use Apache Isis to prototype your
- domain model, because they are just pojos after all. Indeed, embedding
- Isis' metamodel for a deployment hosted on some other framework is the
- objective of the <emphasis>embedded runtime</emphasis> component of
- Isis</para>
- </sect1>
-
- <sect1>
- <title>How extensive is the application security support provided by
- Isis?</title>
-
- <para><emphasis>Apache Isis</emphasis> exposes a pluggable
- authentication <acronym>API</acronym> and an authorization
- <acronym>API</acronym>, with default implementations of both.</para>
-
- <para>The job of the authentication <acronym>API</acronym> is to
- authenticate the user credentials and return the user Id and a set of
- roles for that user. This information is then used in one of two ways.
- The authorization <acronym>API</acronym> uses it to implement
- declarative class-based security, that is, associating access to
- features based on the roles of the user. In addition, though, a domain
- object instance can also access the user credentials, and so can
- implement imperative instance-based checks if required.</para>
-
- <para>For example, we might say that only a user with
- <acronym>HR</acronym> role can award pay rises to an
- <classname>Employee</classname>. This is a class-based check and would
- be implemented through the authorization <acronym>API</acronym>. But,
- we might also say that a user is allowed to view (though not modify)
- their own salary of their <classname>Employee</classname> object, but
- no-one else's. This would be an instance-based check, and would be
- implemented in the object itself.</para>
-
- <para>In terms of how naked objects restricts access, this is done
- either by making an object member (property, collection or action)
- invisible, or, if it is visible, then making it unusable (greyed
- out).</para>
- </sect1>
-
- <sect1>
- <title>How does an Isis applications manage the custom logic in terms
- of business rules, workflow and other business logic that developers
- have to manually implement outside the generated code?</title>
-
- <para>"Business rules" is one of those amorphous terms that means
- different things to different people. A consequence is that the team
- can struggle to nail down exactly where such rules should reside, so
- that these rules can start to leach out of the domain layer and the
- application layer, or even worse, the presentation layer. As noted
- already, one of the principles of naked objects is that domain objects
- are behaviourally complete. What that means is that business rules and
- "other business logic" are the responsibility of domain objects. For
- workflow, again as noted earlier, we suggest that you introduce a view
- model object and have that manage the user's interaction.</para>
-
- <para>However, saying that business rules are an object's
- responsibility doesn't necessarily mean that the implementation has to
- be in Java. We already said that domain objects can delegate work out
- to domain services that are injected into them. So if you want to put
- your business rules into a rules engine, that's fine. Wrap the
- interface to the rules engine within a domain service, and have the
- domain object call out to the domain service in order to fulfil its
- responsibility.</para>
-
- <para>In practical terms, you're going to see business rules both in
- the small-scale and the large. Small-scale business rules tend to be
- implemented in terms of the supporting methods (disable, validate and
- so on) that encode the preconditions for interactions. These ensure
- that the object or the providing arguments is are valid and will veto
- the interaction otherwise (blacklisted customers, out-of-stock
- products). The larger-scale business rules are represented as actions
- on an object that can perform arbitrary business logic, such as
- modifying its own state, or related objects, and/or delegating out to
- domain services.</para>
- </sect1>
-
- <sect1>
- <title>How does Isis support SOA-based applications, eg where an
- enterprise service component is consumed by several different
- applications and other clients?</title>
-
- <para>We can actually answer this both in terms of an Isis application
- consuming <acronym>SOA</acronym><footnote>
- <para>SOA=Service Oriented Architecture</para>
- </footnote> services, and in it <emphasis>providing</emphasis>
- services.</para>
-
- <para>For consuming <acronym>SOA</acronym> services, the answer is
- easy enough: wrap the <acronym>SOA</acronym> service in a domain
- service interface, and then register the implementation with the
- framework so that the service is injected into each domain object.
- That interaction can be either synchronous or asynchronous; there's
- nothing to prevent the domain object publishing an event to be picked
- up by some other component out there on your enterprise service
- bus.</para>
-
- <para>As to providing <acronym>SOA</acronym> services, Isis'
- <acronym>JSON</acronym> viewer exposes the domain objects as a set of
- RESTful resources. We map objects and object members to the standard
- <acronym>HTTP</acronym> verbs, eg so that a GET on an object returns
- an object representation, a PUT on an object's property will modify
- the state, or a POST on an object's actions will invoke the action. As
- its name suggests, the <acronym>JSON</acronym> viewer produces a
- <acronym>JSON</acronym> representation, making it easy to consume (eg
- in HTML5 or RIA applications).</para>
- </sect1>
-
- <sect1>
- <title>Most enterprise applications need to reuse the domain and
- service layer classes between several different applications. How does
- Apache Isis address this?</title>
-
- <para>One way to think of reuse in terms of strategic domain-driven
- design, which couches the discussion in terms of identifying bounded
- contexts and understanding the relationship between these contexts -
- context maps.</para>
-
- <para>You can think of a bounded context as both the user population
- that understands a particular ubiquitous language as well as the
- actual system that they use. For example, the term "Customer" is going
- to mean one thing to the marketing department, but a potentially
- different thing to those in shipping. Those two user populations
- therefore have incompatible languages, and so any integration of
- systems that support both sets of users will need to explicitly map
- their meanings of those terms.</para>
-
- <para>Simplifying somewhat, an Isis (naked objects) application can be
- thought of in three parts: the domain model objects themselves, the
- viewer that provides a channel to interact with the domain model, and
- the domain services that are used by the domain model objects. The
- domain model objects encode a ubiquitous language for a particular
- bounded context, so it only makes sense to reuse them by users who
- have the same understanding of that language. We can if we want use
- view model objects to provide projections of the domain entities to
- different subsets of that user population, but all must agree with
- what a Customer is, even if they use different parts of said
- Customer.</para>
-
- <para>Viewers are channels by which the domain model is accessed.
- <emphasis>Apache Isis</emphasis> has viewers that expose the domain
- model as either a desktop app or as a webapp, and also has a RESTful
- viewer (producing <acronym>JSON</acronym>) which allow the view model
- objects/domain objects to be surfaced as RESTful resources. In
- <acronym>DDD</acronym> terms the RESTful viewers are examples of an
- "open host service" context mapping from some client bounded context
- up to our model's context.</para>
-
- <para>With respect to domain services, these are not reusable per se,
- rather they allow the domain objects to be the client of some other
- service within the enterprise. That said, one common type of domain
- service is to provide an interface to an enterprise service bus, in
- which case the domain objects can publish events onto that bus. In
- <acronym>DDD</acronym> terms this would be an example of a "published
- language" context mapping; the downstream systems consuming the events
- of the publishing domain objects.</para>
-
- <para>Naked objects also allows a somewhat finer-grained level of
- reuse; indeed its support for "don't repeat yourself" is one of the
- reasons that some people are attracted to the pattern. For example, if
- we declare a property or an action parameter to be a date, then the
- viewers will automatically provide a date chooser widget for that
- property or parameter unless indicated otherwise. Or, if a property's
- type is an enum, then we'll get a drop-down box. It's the conventions
- of the naked objects programming model - establishing a protocol of
- interaction between the presentation layer and the domain model - that
- enable this sort of reuse.</para>
-
- <para>Some of Isis' more recent viewers also allow mashups within the
- <acronym>UI</acronym>. For example, the viewer that we've implemented
- using Apache Wicket uses the chain of responsibility pattern to build
- the <acronym>UI</acronym>. If we have an entity can provide its
- <classname>Location</classname> (eg, it implements
- <classname>Locatable</classname>), then a GoogleMapsWidget will render
- the entity in a map. Or, if an entity has a date, then a
- CalendarWidget might render the entity on a calendar. This stuff is
- extensible so you're free to write your own components and reuse (or
- indeed invent your own) domain semantics as you see fit.</para>
- </sect1>
-
- <sect1>
- <title>What is the future road map of the project?</title>
-
- <para>Our proposal to join the incubator is online, and that captured
- the thoughts we had at that point in time <ulink
- url="http://wiki.apache.org/incubator/IsisProposal">[1]</ulink>. A
- general theme is in the ongoing development of the various viewers,
- such as the Wicket viewer, improving JSON support within the RESTful
- viewers, and ongoing development of the taglib-based viewer, Scimpi.
- Contributors who have expressed interest in developing similar viewers
- using JSF/Facelets, Tapestry, on Android and in JavaFX/Visage.</para>
-
- <para>Isis has a pluggable architecture with about five major APIs
- (and numerous more fine-grained APIs) that can be developed. Probably
- the most significant of these is the runtime/persistence
- <acronym>API</acronym>. One piece of work is to develop an ORM-based
- object store from Hibernate (which is not compatible with Apache's) to
- a compatible JPA implementation, We've also had a suggestion [<ulink
- url="https://issues.apache.org/jira/browse/ISIS-14">2</ulink>] to
- re-implement using JDO 3.0/DataNucleus, which would allow us to cover
- a lot of persistence technologies in one fell swoop</para>
-
- <para>Another objective during incubation is to make Isis more easily
- bootstrappable and embeddable, and for that JSR-299 (Contexts and
- Dependency Injection) looks promising. For example, it should be easy
- to take just the <emphasis>Isis</emphasis> metamodel component and use
- it within your own applications, eg to drive the rendering of domain
- objects within custom <acronym>UI</acronym> code.</para>
-
- <para>One other area we might see work is in support for other JVM
- languages. Isis already supports Groovy as well as Java, and the way
- that Isis builds up its metamodel means that supporting other
- languages (Scala, Fantom, Go) ought to be pretty
- straightforward.</para>
-
- <para>Ultimately, though, Apache Isis' roadmap depends on where its
- community wants to take it; so any of the above might change as we
- progress through to graduation.</para>
- </sect1>
- </chapter>
-
- <chapter id="chp.DevelopmentProcess">
- <title>A Development Process</title>
-
- <abstract>
- <para>This chapter describes the general approach to follow that we
- recommend for developing domain-driven applications on Isis.</para>
- </abstract>
-
- <para>There's quite a lot to <emphasis>Apache Isis</emphasis>, with lots
- of optional components, and we do recognize that it may be difficult to
- know how to get going. This chapter outlines a development process which
- you can use as the basis for developing your own way of working.</para>
-
- <sect1 id="sec.ApplicationArchetypes">
- <title>Run the Quickstart Archetype</title>
-
- <para>We tend to organize <emphasis>Isis</emphasis> applications to
- follow a standard structure, and you can use the
- <emphasis>quickstart</emphasis> Maven archetype (see the
- <emphasis>Isis</emphasis> <ulink
- url="http://incubator.apache.org/isis">website</ulink>) to set this up
- for you. This sets up a simple application that can be run
- out-of-the-box using the default runtime and in-memory object store,
- and supporting a number of the webapp viewers. The WAR file can be run
- either standalone from the command line or deployed to a servlet
- container such as Tomcat.</para>
- </sect1>
-
- <sect1>
- <title>Programming Model</title>
-
- <para>Once you've got the archetype application running, you're ready
- to start developing your own domain objects. But no matter what
- application you are developing, you'll need to understand the
- <emphasis>Isis programming model</emphasis>.</para>
-
- <para>The programming model is the set of annotations and programming
- conventions that Isis recognizes. You'll find full details of the
- programming model in this guide; see <xref
- linkend="prt.WritingDomainObjects" />.</para>
-
- <para>In addition, the applib contains a small number of utilities
- which can be useful when writing your application: one such is the
- ability to create XML snapshots of your domain object graphs, see
- <xref linkend="chp.XmlSnapshots" />.</para>
- </sect1>
-
- <sect1>
- <title>Fixtures and Prototyping</title>
-
- <para>We suggest that the fastest way to develop your application is
- to start prototyping using the in-memory object store (set up
- automatically by the quickstart archetype). The nice thing about
- working this way is that there is no database schema to slow you down;
- you can make changes and then rapidly try them out.</para>
-
- <para>On the other hand, the in-memory object store doesn't persist
- objects between runs, so you'll soon tire of continually recreating
- test objects to try out your changes. You should therefore use
- fixtures: blocks of code that are used to setup objects in the
- in-memory object store prior to the app running.</para>
-
- <para>You can find more information about using fixtures in <xref
- linkend="chp.Fixtures" />. It's also worth knowing about fixtures
- because they are used when writing tests for your domain application
- (see <xref linkend="sec.AgileTesting" />).</para>
- </sect1>
-
- <sect1 id="sec.Viewers">
- <title>Viewers</title>
-
- <para><emphasis>Apache Isis</emphasis> allows the same domain object
- model to be consumed by multiple presentation layers. You'll find that
- the quickstart archetype sets up a webapp module that bundles together
- two of the viewers currently available, namely:</para>
-
- <itemizedlist>
- <listitem>
- <para>The <acronym>HTML</acronym> viewer (in
- <filename>viewers/html</filename>) provides a basic webapp.</para>
-
- <para>Other than tweaking <acronym>CSS</acronym>, the views that
- it provides of objects cannot be customized.</para>
- </listitem>
-
- <listitem>
- <para>The <acronym>JSON</acronym> viewer (in
- <filename>viewers/restfulobjects</filename>) which exposes your
- domain objects through a <ulink
- url="http://en.wikipedia.org/wiki/Representational_State_Transfer">RESTful</ulink>
- interface.</para>
-
- <para>The intention of this viewer is to allow programmatic access
- to your domain objects from other (perhaps non-Java)
- clients.</para>
- </listitem>
-
- <listitem>
- <para>The <acronym>BDD</acronym> viewer (in
- <filename>viewers/</filename>bdd) provides an integration with the
- <ulink url="http://concordion.org">Concordion</ulink> BDD
- framework; see <xref linkend="sec.BddTesting" />.</para>
- </listitem>
-
- <listitem>
- <para>The <acronym>JUnit</acronym> viewer (in
- <filename>viewers/junit</filename>) uses wrappers and a custom
- Isis JUnit test class runner (<code>IsisTestRunner</code>); see
- <xref linkend="sec.UnitTesting" />.</para>
- </listitem>
- </itemizedlist>
- </sect1>
-
- <sect1 id="sec.AgileTesting">
- <title>Agile Testing</title>
-
- <para><emphasis>Apache Isis</emphasis> is very much aligned to agile
- development practices, and provides two complementary mechanisms for
- you to test-drive the development of your application. A story test
- (BDD ensures that the right system is built. A unit test ensures that
- the system is built right.</para>
-
- <sect2 id="sec.BddTesting">
- <title>Story (BDD) Testing</title>
-
- <para>Many agile practitioners use story tests as a means to capture
- the acceptance (or completion) criteria for high-level user stories.
- These story tests are typically captured in a non-programmatic form
- so that is understandable to domain experts as well as
- programmers.</para>
-
- <para><emphasis>Apache Isis</emphasis> provides an out-of-the-box
- integration with <ulink
- url="http://concordion.org">Concordion</ulink> (where the story test
- is captured as <acronym>HTML</acronym>).<footnote>
- <para>An integration with <ulink
- url="http://fitnesse.org">FitNesse</ulink> (where the story test
- is captured within a wiki) also exists. Due to licensing this is
- not part of <emphasis>Isis</emphasis> proper.</para>
- </footnote>There is full coverage of how to use this integration
- in the <acronym>BDD</acronym> viewer's
- (<filename>viewers/bdd</filename>) documentation.</para>
- </sect2>
-
- <sect2 id="sec.UnitTesting">
- <title xreflabel="ref.unittesting">Unit Testing</title>
-
- <para>Unlike story tests, unit tests are normally written in a
- programming language, typically in a framework such as <ulink
- url="http://junit.org">JUnit</ulink>.</para>
-
- <para>When writing unit tests, you have a choice. Since all the
- business logic in Isis applications is encapsulated in domain object
- pojos, you can just write unit tests using nothing more than JUnit
- and perhaps also a mocking library such as <ulink
- url="http://jmock.org">JMock</ulink>.</para>
-
- <para>A slightly more sophisticated approach is to use the JUnit
- integrations and supporting classes of the Wrapper programming model
- . The idea of these utilities is to wrap your domain objects in
- proxies that apply the same rules as an <emphasis>Apache
- Isis</emphasis> viewer. For example, if you try to change a property
- or invoke an action that is disabled, then the proxy will throw an
- exception. You write your test to pass if the exception is thrown,
- and failed otherwise (eg using
- <code>@Test(expected=DisabledException.class)</code>).</para>
-
- <para>Full documentation for unit testing support is in the JUnit
- viewer's guide (<filename>viewers/junit</filename>).</para>
- </sect2>
- </sect1>
-
- <sect1>
- <title>A Domain Library</title>
-
- <para>The idea behind the <filename>domain</filename> module is to
- provide some off-the-shelf code for you to use and adapt in your own
- applications. This code is fully tested (comes with tests), and is
- intended to be well-designed. Using code from the library should give
- you a kick-start in writing your own domain applications.</para>
-
- <note>
- <para>The library is currently very modest, but we hope it might
- build up in time.</para>
- </note>
-
- <para>Of course, there's a limit to the complexity of the code that's
- included in the library, because every domain is different. But having
- a full set of tests should allow you to safely refactor the code to
- your own requirements.</para>
-
- <para>There's also no guarantee that the library will contain code for
- your specific requirement. But if you do write some domain logic that
- you think might be reusable by others, why not consider donating it
- back to Isis when you've done so?</para>
-
- <para>The domain library breaks out into three: services, entities and
- values.</para>
-
- <sect2>
- <title>Domain Services</title>
-
- <para><emphasis>Apache Isis</emphasis> applications use
- <emphasis>domain service</emphasis>s (a domain-driven design
- pattern) to allow objects to interact with other domains (or
- <emphasis>bounded context</emphasis>s, to use the correct term).
- These domain services are automatically injected into each domain
- object as it is instantiated.</para>
-
- <para>Domain services split into two: those that interact with
- technical domains (such as sending email, or rendering
- <acronym>PDF</acronym>s), and those that interact with business
- domains (such as general ledger, <acronym>CRM</acronym>s or legacy
- systems).</para>
-
- <para>Obviously domain services that bridge to business domain
- services are always likely to be specific to each individual
- application. So the services in the
- <filename>domain/services</filename> module focus on providing
- off-the-shelf implementations for some of the more common
- <emphasis>technical</emphasis> domain services.</para>
- </sect2>
-
- <sect2>
- <title>Domain Values</title>
-
- <para>As you probably would expect, <emphasis>Apache Isis</emphasis>
- treats Java primitives, Java wrapper classes,
- <classname>java.lang.String</classname>,
- <classname>java.math.BigDecimal</classname> and
- <classname>java.math.BigInteger</classname> as immutable values, It
- allows <classname>java.util.Date</classname> and its subclasses
- (java.sql.Date, java.sql.Time and
- <classname>java.sql.Timestamp</classname>) to be treated as values
- also. In addition, it offers a small number of its own value types,
- such as <classname>Money</classname>.</para>
-
- <para>In addition, it is possible to extend Isis support for value
- types using its <classname>@Value</classname> annotation. We hope in
- time to provide out-of-the-box integrations for popular 3rd party
- libraries such as JodaTime.</para>
- </sect2>
-
- <sect2>
- <title>Domain Entities</title>
-
- <para>In the future we hope that Isis will provide some well-defined
- and ready-tested domain entities for use in your systems. For
- example, a set of entities to model customers
- (<classname>Customer</classname>/<classname>Name</classname>/<classname>Address</classname>)
- might be useful in many systems.</para>
- </sect2>
- </sect1>
-
- <sect1 id="sec.DeployingAnIsisApplication">
- <title>Deploying an Isis Application</title>
-
- <para>There are two main decisions to make in deploying an
- <emphasis>Isis</emphasis> application.</para>
-
- <sect2>
- <title>Persistence (the Default Runtime / Object Store)</title>
-
- <para>In the same way that viewers provide pluggability for the
- "front-end", <emphasis>Apache Isis</emphasis> also offers
- pluggability on the back-end, i.e. persistence.</para>
-
- <para>In fact, there are two decisions to be made here: which
- runtime to use, and (if using the default runtime), which object
- store to use. The default runtime is the one originally developed
- within the <emphasis>Naked Objects Framework</emphasis>, and
- therefore has been inherited by <emphasis>Apache
- Isis</emphasis>.<footnote>
- <para>At the time of writing the default runtime is the only
- real runtime available; however we expect to be developing
- lighter-weight replacements for the default runtime in the
- future.</para>
- </footnote>The default runtime is also the one configured by the
- quickstart archetype.</para>
-
- <para>If using the default runtime, you have the option of choosing
- the objectstore. As already discussed, the quickstart archetype
- configures the in-memory objectstore by default, which is great for
- rapid prototyping. However, at some point you will need to integrate
- with a "real" object store that persists object to some sort of
- serialized persistence.</para>
-
- <para>Of the object stores to provided by the default runtime, some
- are easy to configure, some more complex; some are only suitable for
- single-user apps, others for multi-users. Each of the object stores
- has its own documentation, so you can select the correct object
- store to choose:<footnote>
- <para>In addition to those listed here, there have also been
- objectstores for JPA (using Hibernate) and BerkeleyDB. These are
- currently not part of Apache Isis distribution because of
- incompatible licenses. Support for an ORM will in the future be
- provided either by porting the JPA object store to the default
- runtime, or, we may support it via a JDO-based new
- runtime.</para>
- </footnote></para>
-
- <itemizedlist>
- <listitem>
- <para>the <acronym>XML</acronym> object store (in
- <filename>objectstores/xml</filename>) is designed for
- single-user systems, and persists to its own internal
- (proprietary) <acronym>XML</acronym> format;</para>
- </listitem>
-
- <listitem>
- <para>the <acronym>SQL</acronym> object store (in
- <filename>objectstores/sql</filename>) is a multi-user object
- store that persists to an <acronym>RDBMS</acronym> (direct over
- <acronym>JDBC</acronym>);</para>
- </listitem>
-
- <listitem>
- <para>the <acronym>NoSQL</acronym> object store (in
- <filename>objectstores/nosql</filename>) is a multi-user object
- store that persists to NoSQL databases in JSON form.</para>
- </listitem>
- </itemizedlist>
-
- <para>You'll also find coverage of the object store
- <acronym>API</acronym> itself in the default runtime's
- documentation.</para>
-
- <para>Alternatively, the default runtime can be configured to
- support client/server remoting. This configuration only makes sense
- for the <acronym>DnD</acronym> viewer, whereby it is configured as
- the client and a separate instance of Isis runs as the server.
- Again, full guidance is available in the default runtime's
- documentation.</para>
- </sect2>
-
- <sect2>
- <title>Authentication and Authorization</title>
-
- <para><emphasis>Apache Isis</emphasis> provides an
- <acronym>API</acronym>s for both authentication and
- authorization.</para>
-
- <para>The core implementation of these <acronym>API</acronym>s are
- simple basic noop-based authentication and authorization mechanisms.
- For deployment into production, you'll need to configure with
- another alternative implementation.</para>
-
- <para>One option is the file-based implementation (in
- <filename>security/file</filename>), that stores the security
- information in flat files. This is simple, but unlikely to be robust
- enough for enterprise use.</para>
-
- <para>Another alternative is to use the <acronym>LDAP</acronym>
- implementation (in <filename>security/ldap</filename>), which can
- integrate with an <acronym>LDAP </acronym>infrastructure if you have
- one. If you have a different security infrastructure, then you might
- consider to write your own implementation (the
- <acronym>API</acronym> is not complex).</para>
- </sect2>
- </sect1>
- </chapter>
- </part>
-
<part id="prt.WritingDomainObjects">
<title>Writing Domain Objects</title>
@@ -1089,7 +119,8 @@
linkend="chp.ValueTypes" />.</para>
<sect1>
- <title>How to not inherit from framework superclasses</title>
+ <title>How to have entities be POJOs (not inherit from framework
+ superclasses)</title>
<para>It isn't mandatory for domain entities to inherit from any
framework superclass; they can be plain old java objects (pojos) if
@@ -1112,726 +143,487 @@
</sect1>
<sect1>
- <title condition="java">How to specify a title for an object</title>
+ <title>How to add a property to an object</title>
- <para>A title is used to identify an object to the user in the user
- interface. For example, a <classname>Customer</classname>'s title
- might be the organization's customer reference, or perhaps (more
- informally) their first and last names.</para>
+ <para>Properties are specified using the JavaBean conventions,
+ recognizing a standard accessor/mutator pair (<code>get</code> and
+ <code>set</code>).</para>
- <para>By default, the framework will use the object's <literal
- moreinfo="none">toString()</literal> method as the title. Most titles
- tend to be made up of the same set of elements: for example a
- Customer's name might be the concatenation of their customer first
- name and their ;ast name. For these the <classname>@Title</classname>
- annotation can be used:</para>
+ <para>The syntax is:</para>
- <para><programlisting>public class Customer {
- @Title
- public String getFirstName() { ... }
- @Title
- public String getLastName() { ... }
- ...
-}</programlisting></para>
+ <para><programlisting>public PropertyType getPropertyName()
- <para>For more control, the order of the title components can be
- specified using a sequence number (specified in Dewey decimal
- format):</para>
+public void setPropertyName(PropertyType param)</programlisting></para>
- <programlisting>public class Customer {
- @Title("1.0")
- public String getFirstName() { ... }
- @Title("1.1")
- public String getLastName() { ... }
- ...
-}</programlisting>
+ <para>where <literal moreinfo="none">PropertyType</literal> is a
+ primitive, a value object or an entity object.</para>
- <para>For more control the title can be declared imperately using the
- <literal moreinfo="none">title()</literal> method (returning a
- <literal moreinfo="none">String</literal>). This leaves the programmer
- needs to make use of the <literal moreinfo="none">toString()</literal>
- method for other purposes, such as for debugging. For example, to
- return the title for a customer which is their last name and then
- first initial of their first name, we could use:</para>
+ <para>Properties may either be for a value type or may reference
+ another entity. Values include Java primitives, and JDK classes with
+ value semantics (eg <literal
+ moreinfo="none">java.lang.Strings</literal> and <literal
+ moreinfo="none">java.util.Dates</literal>; see <xref
+ linkend="chp.ValueTypes" /> for the full list). It is also possible to
+ write your own value types (see <xref linkend="chp.ValueTypes" />). A
+ property referencing another domain object is sometimes called an
+ association.</para>
- <para><programlisting>public class Customer {
- public String title() {
- return getLastName() + ", " + getFirstName().substring(0,1);
- }
- ...
-}</programlisting></para>
+ <para>For example, the following example contains both a value
+ (<literal moreinfo="none">String</literal>) property and a reference
+ (<literal moreinfo="none">Organisation</literal>) property:</para>
- <para>The applib contains a class,
- <classname>org.apache.isis.applib.util.TitleBuffer</classname>, which
- you can use to help create title strings if you so wish. See <xref
- linkend="apx.UtilityClasses" /> for more details.</para>
- </sect1>
+ <programlisting format="linespecific">public class Customer {
- <sect1 id="sec.HowToSpecifyTheIconForAnObjectsClass">
- <title condition=".net">How to specify the icon for an object's
- class</title>
+ private String firstName;
+ public String getFirstName() {
+ return firstName;
+ }
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
- <para condition="1.5">By default, the framework will look for an image
- in the <filename class="directory" moreinfo="none">images</filename>
- directory (either from the classpath or from the filesystem) that has
- the same name as the object class. Multiple file extensions are
- searched for, including <filename>.png</filename>,
- <filename>.gif</filename> and <filename>.jpg</filename> (in order of
- preference). For example, fan object of type
- <classname>Customer</classname> it will look for <filename
- class="directory" moreinfo="none">Customer.png</filename>, <filename
- class="directory" moreinfo="none">Customer.gif</filename>,
- <filename>Customer.jpg</filename> etc.</para>
- <para condition="1.5">If the framework finds no such file, then it
- will work up the inheritance hierarchy to see if there is an icon
- matching the name of any of the super-classes, and use that instead.
- If no matching icon is found then the framework will look for an image
- called <filename class="directory"
- moreinfo="none">default.png</filename>,
- <filename>default.gif</filename> or <filename>default.jpg</filename>
- in the images directory, and if this has not been specified, then the
- framework will use its own default image for an icon.</para>
+ private Organisation organisation;
+ public Organisation getOrganisation() {
+ return organisation;
+ }
+ public void setOrganisation(Organisation organisation) {
+ this.organisation = organisation;
+ }
- <para>We strongly recommend that you adopt 'pascal case' as the
- convention for icon file names: if you have a class called <classname
- condition="vb"> OrderLine</classname>, then call the icon <filename
- class="directory" moreinfo="none">OrderLine.png</filename>. Actually,
- the framework will also recognise <filename class="directory"
- moreinfo="none">orderline.png</filename>, but some operating systems
- and deployment environments are case sensitive, so it is good practice
- to adopt an unambiguous convention.</para>
-
- <para>Alternatively, you can use the <literal
- moreinfo="none">iconName</literal>() method instead:</para>
-
- <programlisting format="linespecific">public String iconName() {
- return "Person";
+ ...
}</programlisting>
-
- <para>This makes it easy for more than one class to use the same icon,
- without having to duplicate the image file.</para>
</sect1>
<sect1>
- <title>How to specify the icon for an individual object's
- state</title>
-
- <para>As discussed in <xref
- linkend="sec.HowToSpecifyTheIconForAnObjectsClass" />, the
- <code>iconName()</code> method may be used to specify an object. The
- value returned from this method need not be static, and so it can be
- used to represent the state of an individual object.</para>
+ <title>How to add a collection to an object</title>
- <para>For example, an instance of <classname>Product</classname> could
- use a photograph of the product as an icon, using:</para>
+ <para>A collection is recognized via an accessor/mutator method pair
+ (<code>get</code> and set) for any type of collection provided by the
+ programming language.</para>
- <programlisting format="linespecific">public class Product {
- public String iconName() {
- return "Product-" + getPhotograph();
- }
- ...
-}</programlisting>
+ <para>The syntax is either:</para>
- <para>Alternatively, an <classname>Order</classname> might vary the
- icon according to the status of the object:<programlisting>public class Order {
- public String iconName() {
- return "Order-" + getStatus();
- }
- ...
-}</programlisting></para>
- </sect1>
+ <para><programlisting>public Collection<EntityType> getCollectionName()
+private void setCollectionName(Collection<EntityType> param)</programlisting></para>
- <sect1>
- <title condition="j#">How to specify a name and/or description for an
- object</title>
+ <para>or:</para>
- <para>By default, the name (or type) of an object, as displayed to the
- user will be the class name. However, if an <literal
- moreinfo="none">@Named</literal> annotation is included, then this
- will override the default name. This might be used to include
- punctuation or other characters that may not be used within a class
- name, or when - for whatever reason - the name of the class includes
- technical artifacts (for example project-defined prefixes or
- suffices). It is also useful if the required name cannot be used
- because it is a keyword in the language.</para>
+ <para><programlisting>public List<EntityType> getCollectionName()
+private void setCollectionName(List<EntityType> param)</programlisting></para>
- <para>By default the framework will create a plural version of the
- object name by adding an 's' to singular name, or a 'ies' to names
- ending 'y'. For irregular nouns or other special case, the
- <code>@Plural</code> annotation may be used to specify the plural form
- of the name explicitly.t</para>
+ <para>or:</para>
- <para>The programmer may optionally also provide a <literal
- moreinfo="none">@DescribedAs</literal> annotations, containing a brief
- description of the object's purpose, from a user perspective. The
- framework will make this available to the user in a form appropriate
- to the user interface style - for example as a tooltip.</para>
+ <para><programlisting>public Set<EntityType> getCollectionName()
+private void setCollectionName(Set<EntityType> param)</programlisting></para>
- <para>For example:</para>
+ <para>A mutator is required, but it need only have
+ <code>private</code> visibility.</para>
- <programlisting>@Named("Customer")
-@Plural("Customers")
-@DescribedAs("Individuals or organizations that have either "+
- "purchased from us in the past or "+
- "are likely to in the future")
-public class CustomerImpl implements ICustomer {
- ...
-}</programlisting>
+ <para>Note:</para>
<note>
- <para>There is an entirely separate mechanism for dealing with
- Internationalisation, details of which can be found in the core
- documentation.</para>
+ <para><classname>Map</classname>s cannot be used for
+ collections.</para>
</note>
- </sect1>
-
- <sect1>
- <title condition="vb">How to specify that an object should not be
- persisted</title>
- <para>Non-persisted objects are intended to be used as view models;
- they aggregate some state with respect to a certain process. This may
- be read-only (eg a projection of certain informaiton) or read-write
- (eg a wizard-like process object). Either way, the viewer is expected
- to interpret this by not providing any sort of automatic "save" menu
- item if such an object is returned to the
- <acronym>GUI</acronym>.</para>
-
- <para>Non-persisted objects that are read-only are typically also
- marked as immutable (see <xref linkend="sec.Immutable" />).</para>
-
- <para>To indicate that an object cannot be persisted, use the <literal
- moreinfo="none">@NotPersistable</literal> annotation.</para>
- </sect1>
-
- <sect1 id="sec.Immutable">
- <title id="sec.entity.immutable">How to specify that an object should
- never be modified by the user</title>
-
- <para>Some objects have state which should not be modifiable; for
- example those representing reference data. The viewer is expected to
- interpret this by which suppressing any sort of "edit" button.</para>
-
- <para>To indicate that an object cannot be modified, use the <literal
- moreinfo="none">@Immutable</literal> annotation.</para>
+ <para>It is recommended that the collections be specified using
+ generics (for example: <literal
+ moreinfo="none">List<Customer></literal> ). That way the
+ framework will be able to display the collection based on that type
+ definition. If a raw type is used then the framework will attempt to
+ infer the type from the addToXxx() / removeFromXxx() supporting
+ methods, if specified (see <xref linkend="sec.AddToRemoveFrom" />). If
+ the framework is unable to determine the type of the collection, it
+ will mean that some viewers will represent the collection is a less
+ sophisticated manner (eg a simple list of titles rather than a
+ table).</para>
<para>For example:</para>
- <programlisting>@Immutable
-public class ChasingLetter implements PaymentReclaimStrategy {
+ <programlisting format="linespecific">public class Employee { ... }
+
+public class Department {
+ private List<Employee> employees = new ArrayList<Employee>();
+ public List <Employee> getEmployees() {
+ return employees;
+ }
+ private void setEmployees(List<Employee> employees) {
+ this.employees = employees;
+ }
...
}</programlisting>
-
- <para>See also <xref linkend="sec.Entity.Disabled" />.</para>
</sect1>
- <sect1 id="sec.Entity.Disabled">
- <title>How to control when an object can be modified</title>
+ <sect1>
+ <title>How to add an action to an object</title>
- <para>Some objects have state which should not be modifiable only
- under certain conditions; for example an invoice can not be modified
- after it has been paid. The viewer is expected to interpret this by
- suppressing any sort of "edit" button.</para>
+ <para>By default, any <literal moreinfo="none">public</literal>
+ instance method that you add to a class will be treated as a user
+ action, unless it represents a property, collection, or another
+ reserved method defined in this guide.</para>
- <para>To indicate that an object cannot be modified, use the <literal
- moreinfo="none">String disabled(Type type)</literal> method.</para>
+ <para>The syntax is:</para>
- <para>For example:</para>
+ <para><programlisting>public void actionName([ValueOrEntityType param] ...)</programlisting></para>
- <programlisting>public class FeeInvoice implements Invoice {
- public String disabled(Type type){
- ...
- }
-}</programlisting>
+ <para>or</para>
- <para>The Type is from
- <classname>org.apache.isis.applib.Identifier</classname>:</para>
+ <para><programlisting>public ReturnType actionName([ValueOrEntityType param] ...)</programlisting></para>
- <programlisting> /**
- * What type of feature this identifies.
- */
- public static enum Type {
- CLASS, PROPERTY_OR_COLLECTION, ACTION
- }</programlisting>
+ <para>When a method returns a reference the viewer will attempt to
+ display that object. If the return value is <code>null</code> then
+ nothing is displayed.</para>
- <para>and provides fine grain control over disabling actions and
- properties.</para>
+ <para>We refer to all methods that are intended to be invoked by users
+ as 'action methods'.</para>
- <para>The return String is null if the the object (action or property)
- is not disabled, or the reason why it is disabled, similar to <xref
- linkend="sec.DisabledProperty" />.</para>
+ <para>If you have a method that you don't want to be made available as
+ a user-action you can either:</para>
- <para>See also <xref linkend="sec.Entity.Disabled" />.</para>
- </sect1>
+ <itemizedlist>
+ <listitem>
+ <para>make it non-<literal>public</literal> (eg <literal
+ moreinfo="none">protected</literal> or <literal
+ moreinfo="none">private</literal>)</para>
+ </listitem>
- <sect1 id="sec.Entity.Hidden">
- <title>How to control when an object is visible</title>
+ <listitem>
+ <para>annotate it with <classname>@Ignore</classname></para>
+ </listitem>
- <para>To when an object is visible, provide a hidden() method:</para>
+ <listitem>
+ <para>annotate it with <classname>@Hidden</classname> (discussed
+ further in <xref linkend="sec.HiddenActions" />)</para>
+ </listitem>
+ </itemizedlist>
- <para><programlisting>public class TrackingAction implements Tracking {
- public boolean hidden(){
- ...
- }
-}</programlisting>If the function returns true, all properties and methods
- will be hidden from the user, similar to <xref
- linkend="sec.HiddenProperty" />.</para>
+ <para>Note also that <literal moreinfo="none">static</literal> methods
+ are ignored: such functionality should reside in a service, such as a
+ repository or factory (see <xref
+ linkend="chp.DomainServices" />).</para>
</sect1>
- <sect1 id="sec.Bounded">
- <title>How to specify that a class of objects has a limited number of
- instances</title>
+ <sect1>
+ <title condition="java">How to specify a title for an object</title>
- <para>Sometimes an entity may only have a relatively small number of
- instances, for example the types of credit cards accepted (Visa,
- Mastercard, Amex). Viewers will typically expected to render the
- complete set of instances as a drop down list whenever the object type
- is used (ie as a property or action parameter).</para>
+ <para>A title is used to identify an object to the user in the user
+ interface. For example, a <classname>Customer</classname>'s title
+ might be the organization's customer reference, or perhaps (more
+ informally) their first and last names.</para>
- <para>To indicate that a class has a limited number of instances, use
- the <classname>@Bounded</classname> annotation. Note that there is an
- implied expectation is that the list of objects is small, and
- relatively cheap to obtain from the object store.</para>
+ <para>By default, the framework will use the object's <literal
+ moreinfo="none">toString()</literal> method as the title. Most titles
+ tend to be made up of the same set of elements: for example a
+ Customer's name might be the concatenation of their customer first
+ name and their ;ast name. For these the <classname>@Title</classname>
+ annotation can be used:</para>
- <para>An alternative way to specify a selection of objects is using
- the <classname>choicesXxx()</classname> supporting methods.</para>
+ <para><programlisting>public class Customer {
+ @Title
+ public String getFirstName() { ... }
+ @Title
+ public String getLastName() { ... }
+ ...
+}</programlisting></para>
- <para>For example:</para>
+ <para>For more control, the order of the title components can be
+ specified using a sequence number (specified in Dewey decimal
+ format):</para>
- <programlisting>@Bounded
-public class PaymentMethod {
- ...
+ <programlisting>public class Customer {
+ @Title("1.0")
+ public String getFirstName() { ... }
+ @Title("1.1")
+ public String getLastName() { ... }
+ ...
}</programlisting>
- <para>Alternatively, you might want to use a (Java) <code>enum</code>,
- because these are implicitly bounded.</para>
- </sect1>
-
- <sect1 id="sec.HowToCreateAnObject">
- <title>How to create or delete objects within your code</title>
-
- <para>When you create any domain object within your application code,
- it is important that the framework is made aware of the existence of
- this new object - in order that it may be persisted to the object
- store, and in order that any services that the new object needs are
- injected into it.</para>
-
- <para>Just specifying <literal moreinfo="none">new
- Customer()</literal>, for example, will create a
- <classname>Customer</classname> object, but that object will
- <emphasis>not</emphasis> be known to the framework. However, since we
- do not want to tie our domain objects to a particular framework, we
- use the idea of a 'container' to mediate, specified by the <literal
- moreinfo="none">org.apache.isis.applib.DomainObjectContainer</literal>
- interface. See <xref linkend="apx.DomainObjectContainer" /> for the
- full list of methods provided by
- <classname>DomainObjectContainer</classname>.</para>
-
- <para>This interface defines the following methods for managing domain
- objects:</para>
-
- <itemizedlist>
- <listitem>
- <para><literal moreinfo="none"><T> T
- newTransientInstance(final Class<T>
- ofClass)</literal></para>
-
- <para>Returns a new instance of the specified class, that is
- transient (unsaved). The object may subsequently be saved either
- by the user invoking the Save action (that will automatically be
- rendered on the object view) or programmatically by calling
- <literal moreinfo="none">persist(Object
- transientObject)</literal></para>
- </listitem>
-
- <listitem>
- <para><methodname><T> T newPersistentInstance(final
- Class<T> ofClass)</methodname></para>
-
- <para>Creates a new object already persisted.</para>
- </listitem>
-
- <listitem>
- <para><methodname>boolean isPersistent()</methodname></para>
-
- <para>Checks whether an object has already been persisted. This is
- often useful in controlling visibility or availability of
- properties or actions.</para>
- </listitem>
-
- <listitem>
- <para><methodname>void persist(Object
- transientObject)</methodname></para>
-
- <para>Persists a transient object (created using
- <methodname>newTransientInstance(...)</methodname>, see
- above).</para>
- </listitem>
+ <para>For more control the title can be declared imperately using the
+ <literal moreinfo="none">title()</literal> method (returning a
+ <literal moreinfo="none">String</literal>). This leaves the programmer
+ needs to make use of the <literal moreinfo="none">toString()</literal>
+ method for other purposes, such as for debugging. For example, to
+ return the title for a customer which is their last name and then
+ first initial of their first name, we could use:</para>
- <listitem>
- <para><methodname>void persistIfNotAlready(Object
- domainObject)</methodname></para>
+ <para><programlisting>public class Customer {
+ public String title() {
+ return getLastName() + ", " + getFirstName().substring(0,1);
+ }
+ ...
+}</programlisting></para>
- <para>It is an error to persist an object if it is already
- persistent; this method will persist only if the object is not
- already persistent (otherwise it will do nothing).</para>
- </listitem>
+ <para>The applib contains a class,
+ <classname>org.apache.isis.applib.util.TitleBuffer</classname>, which
+ you can use to help create title strings if you so wish. See <xref
+ linkend="apx.UtilityClasses" /> for more details.</para>
+ </sect1>
- <listitem>
- <para><methodname>void remove(Object
- persistentObject)</methodname></para>
+ <sect1 id="sec.HowToSpecifyTheIconForAnObjectsClass">
+ <title condition=".net">How to specify the icon for an object's
+ class</title>
- <para>Removes (deletes) from the object store, making the
- reference transient.</para>
- </listitem>
+ <para condition="1.5">By default, the framework will look for an image
+ in the <filename class="directory" moreinfo="none">images</filename>
+ directory (either from the classpath or from the filesystem) that has
+ the same name as the object class. Multiple file extensions are
+ searched for, including <filename>.png</filename>,
+ <filename>.gif</filename> and <filename>.jpg</filename> (in order of
+ preference). For example, fan object of type
+ <classname>Customer</classname> it will look for <filename
+ class="directory" moreinfo="none">Customer.png</filename>, <filename
+ class="directory" moreinfo="none">Customer.gif</filename>,
+ <filename>Customer.jpg</filename> etc.</para>
- <listitem>
- <para><methodname>void removeIfNotAlready(Object
- domainObject)</methodname></para>
+ <para condition="1.5">If the framework finds no such file, then it
+ will work up the inheritance hierarchy to see if there is an icon
+ matching the name of any of the super-classes, and use that instead.
+ If no matching icon is found then the framework will look for an image
+ called <filename class="directory"
+ moreinfo="none">default.png</filename>,
+ <filename>default.gif</filename> or <filename>default.jpg</filename>
+ in the images directory, and if this has not been specified, then the
+ framework will use its own default image for an icon.</para>
- <para>It is an error to remove an object if it is not persistent;
- this method will remove only if the object is known to be
- persistent (otherwise it will do nothing).</para>
- </listitem>
- </itemizedlist>
+ <para>We strongly recommend that you adopt 'pascal case' as the
+ convention for icon file names: if you have a class called <classname
+ condition="vb"> OrderLine</classname>, then call the icon <filename
+ class="directory" moreinfo="none">OrderLine.png</filename>. Actually,
+ the framework will also recognise <filename class="directory"
+ moreinfo="none">orderline.png</filename>, but some operating systems
+ and deployment environments are case sensitive, so it is good practice
+ to adopt an unambiguous convention.</para>
- <para>A domain object specifies that it needs to have a reference to
- the <classname>DomainObjectContainer</classname> injected into by
- including the following code:</para>
+ <para>Alternatively, you can use the <literal
+ moreinfo="none">iconName</literal>() method instead:</para>
- <programlisting format="linespecific">private DomainObjectContainer container;
-protected DomainObjectContainer getContainer() {
- return container;
-}
-public final void setContainer(final DomainObjectContainer container) {
- this.container = container;
+ <programlisting format="linespecific">public String iconName() {
+ return "Person";
}</programlisting>
- <para>Creating or deleting objects is then done by invoking those
- methods on the container. For example the following code would then
- create a new <classname>Customer</classname> object within another
- method:</para>
-
- <programlisting format="linespecific">Customer newCust = getContainer().newTransientInstance(Customer.class);
-newCust.setName("Charlie");
-getContainer().persist(newCust);</programlisting>
-
- <para>If you are able to make your domain object inherit from <literal
- moreinfo="none">org.apache.isis.applib.AbstractDomainObject</literal>
- then you have direct access to those methods, so the code would
- become:</para>
-
- <programlisting format="linespecific">Customer newCust = newTransientInstance(Customer.class);
-newCust.setName("Charlie");
-persist(newCust);</programlisting>
-
- <para>As an alternative to putting the creation logic within your
- domain objects, you could alternatively delegate to an injected
- factory (see <xref linkend="chp.DomainServices" />). Ultimately
- factories just delegate back to
- <classname>DomainObjectContainer</classname> in the same way, so from
- a technical standpoint there is little difference. However it is
- generally worth introducing a factory because it provides a place to
- centralize any business logic. It also affords the opportunity to
- introduce a domain term (eg <classname>ProductCatalog</classname> or
- <classname>StudentRegister</classname>), thereby reinforcing the
- "ubiquitous language".</para>
-
- <para>These methods are actually provided by the
- <classname>org.apache.isis.applib.AbstractContainedObject</classname>
- and so are also available on <literal
- moreinfo="none">org.apache.isis.applib.AbstractService</literal> (and,
- hence, on <literal
- moreinfo="none">org.apache.isis.applib.AbstractFactoryAndRepository</literal>)
- for creating objects within a service.</para>
-
- <warning>
- <para>It is possible to create a transient object within another
- transient object. When the framework persists any transient object,
- by default it will automatically persist any other transient object
- referenced by that object. However, if any of these transient
- objects are to be exposed to the user (while in their transient
- state), then you need to write your code very carefully -
- anticipating the fact that the user could elect to save any of the
- transient objects at any point - which could cause the graph of
- related objects to be persisted in an invalid state.</para>
-
- <para>The recommended approach is, if possible, to mark these
- supplementary classes as not persistable by the user (see <xref
- linkend="not-persistable" />), or not to permit the user to create a
- new transient object that is a child of an existing transient
- object, but, rather, to require the user to save the parent object
- first.</para>
- </warning>
+ <para>This makes it easy for more than one class to use the same icon,
+ without having to duplicate the image file.</para>
</sect1>
- <sect1 id="sec.LifecycleMethods">
- <title>How to insert behaviour into the object life cycle</title>
-
- <para><emphasis>Apache Isis</emphasis> is responsible for managing the
- object lifecycle, persisting, updating or removing objects from the
- persistent object store as required. For many applications the domain
- objects are unaware of this. If required, though, an object can
- provide callback methods (all optional) so that the framework can
- notify it of its persistence state.</para>
-
- <para>For example, the <literal
- moreinfo="none"><methodname>persisted</methodname></literal>() method
- is called after an object has been persisted. This could be used to
- setup a reverse association that should only be created once the new
- object has been persisted.</para>
-
- <para>The full list of callbacks is shown below.</para>
-
- <table>
- <title>Object lifecycle methods</title>
-
- <tgroup cols="2">
- <colspec align="left" colwidth="105" />
-
- <colspec align="left" />
-
- <thead>
- <row>
- <entry align="center">Method</entry>
-
- <entry align="center">When called by framework</entry>
- </row>
- </thead>
-
- <tbody>
- <row>
- <entry><methodname>created()</methodname></entry>
-
- <entry>following the logical creation of the object (that is,
- after <methodname>newTransientInstance()</methodname> has been
- called)</entry>
- </row>
-
- <row>
- <entry><methodname>loading()</methodname></entry>
-
- <entry>when a persistent object is about to be loaded into
- memory</entry>
- </row>
-
- <row>
- <entry><methodname>loaded()</methodname></entry>
-
- <entry>once the persistent object has just been loaded into
- memory</entry>
- </row>
-
- <row>
- <entry><methodname>persisting()</methodname> or
- <methodname>saving()</methodname></entry>
-
- <entry>just before a transient object is first
- persisted.</entry>
- </row>
-
- <row>
- <entry><methodname>persisted()</methodname> or
- <methodname>saved()</methodname></entry>
+ <sect1 id="sec.MemberOrderForProperties">
<TRUNCATED>