@Action(
invokeOn = InvokeOn.OBJECT_AND_COLLECTION,
publishing = Publishing.ENABLED
)
public Invoice approve() {
setStatus(InvoiceStatus.APPROVED);
return this;
}
Dan Haywood & Eoin Woods
Eoin Woods is CTO at Endava, a European IT services company. He is also the author of “Software Systems Architecture”
Dan Haywood is an independent consultant & developer specializing in domain driven design. He is an Apache member and PMC chair for Apache Isis
We met and worked together in the 90s at Sybase, and have (as friends) tracked each other’s respective careers ever since.
Long lived applications suffer from architectural problems.
the architectural style erodes until it is hard to discern
not clear how new features fit in the architecture
pattern implementations weaken and become unclear
coherency weakens due to adhoc architecture evolution
Change becomes difficult as implementation and architecture drift apart due to them being different artefact groups.
Business logic drifts into adjacent layers
Domain model becomes unwieldy ("tactical" changes)
Aspects of the architecture forgotten or misinterpreted
Rushed changes create mismatched components
New components difficult to test, deploy or support
Utility modules grow in size and scope
Decision rationale forgotten leading to poor evolution
The Apache Isis framework is an implementation of the naked objects pattern
Named & developed by Richard Pawson and Rob Matthews
further details in Richard’s "Naked Objects" PhD thesis
external examiner was Trygve Reenskaug
Generic UI provides a framework generated UI "for free"
A good UI is expensive and difficult to get right
UI and UX skills relatively rare in most organisations
Difficult to maintain a consistent UI at scale
Avoiding custom code eases upgrade & replacement
Generated UI prevents accidental coupling
Tradeoff is the difficulty of supporting special cases
… though it has, admittedly, been a journey to get them to that level!
Estatio is an invoicing application for tenants within a shopping centre
Developed for Eurocommercial Properties, deployed to the three operating countries (Italy, France & Sweden)
The code also happens to be open source (on github)
A useful "reference example" for the Apache Isis user community
Generic UI can have advantages in development efficiency, UX consistency and future proofing. What are the trade offs?
How much control can an application have on its UI?
Can layout, look-and-feel, flow, colour be changed easily?
What if an application needs a new widget type entirely?
Use annotations, provide UI hints
eg @ActionLayout
(vs @Action
), @MemberOrder
Optional .layout.xml
for each concrete class
basically a DSL, modelled on Bootstrap
reloaded dynamically
CSS
classes and Ids generated from the metamodel
Plugins to render maps, calendars, export as Excel etc.
a generic UI treats the UI as a cross-cutting concern
enabled by the magic of a meta-model, other concerns can be tackled too
check & enforce patterns or constraints
add security automatically, audit trail
interface specifications, eg Swagger
i18n
Like a standardised version of aspects
The "system of systems" forming the technology environment for an organisation. Concerns include:
What is the functional scope of each system?
How do systems interact to support business processes?
How is data stored, mastered, owned, changed, accessed?
What technical standardisation is available/valuable?
Does Isis help the evolution of an enterprise architecture?
Each system should have clear responsibilities
A bespoke app (as opposed to COTS systems) can be tailored precisely for its user base
Multiple hooks (SPIs) at the application layer, domain layer, persistence layer
In particular, interactions can be automatically published to other systems in the enterprise
Each interaction (action invocation or property edit) can be reified into XML
persisted and then published onto an event bus, eg Apache Camel
we recommend "skinny" events, better separation of responsibilities
Also useful for profiling, auditing, replay/regressions
correlated with audit trail (ie cause/effect)
@Action(
invokeOn = InvokeOn.OBJECT_AND_COLLECTION,
publishing = Publishing.ENABLED
)
public Invoice approve() {
setStatus(InvoiceStatus.APPROVED);
return this;
}
public interface PublisherService {
void publish(Interaction.Execution<?, ?> execution);
}
<ixn:interactionDto xmlns:com="http://isis.apache.org/schema/common"
xmlns:cmd="http://isis.apache.org/schema/cmd"
xmlns:ixn="http://isis.apache.org/schema/ixn">
<ixn:transactionId>929c2e7a-6318-4f4c-ab7b-96b3632d6731</ixn:transactionId>
<ixn:execution xsi:type="ixn:actionInvocationDto"
interactionType="action_invocation"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ixn:sequence>0</ixn:sequence>
<ixn:target type="org.estatio.dom.invoice.Invoice" id="1"/>
<ixn:memberIdentifier>org.estatio.dom.invoice.Invoice#approve()</ixn:memberIdentifier>
<ixn:parameters/>
<ixn:returned type="reference" null="false">
<com:reference type="org.estatio.dom.invoice.Invoice" id="1"/>
</ixn:returned>
<ixn:user>estatio-admin</ixn:user>
<ixn:title>org.estatio.dom.invoice.Invoice:1: approve()</ixn:title>
<ixn:metrics>
<ixn:timings>
<com:startedAt>2016-09-30T13:57:19.825+01:00</com:startedAt>
<com:completedAt>2016-09-30T13:57:19.832+01:00</com:completedAt>
</ixn:timings>
<ixn:objectCounts>
<ixn:loaded before="4" after="6"/>
<ixn:dirtied before="0" after="1"/>
</ixn:objectCounts>
</ixn:metrics>
</ixn:execution>
</ixn:interactionDto>
Most projects build their own architecture framework
This provides a lot of flexibility and control…
… but also a lot of work
… and the constant potential for inconsistency
By accepting the constraints of Apache Isis, the "architecture" comes for free and enforces true modularity and separation of concerns
dan@haywood-associates.co.uk and @dkhaywood
eoin.woods@endava.com and @eoinwoodz
slide deck at github.com/danhaywood/jaxlondon2016
Architecture & code diverge when it is hard to remember and implement the architectural constraints
Constraints are there to reduce implementation freedom
… and allow focus on what is important
Hard to know how architectural constraints are realised
Frameworks such as Apache Isis aim to embody the constraints within the framework
Try to make the "right" thing the "easy" thing
Department of Social Protection, Ireland
Implemented a Naked Objects system in 2004
Originally just for pensions and child benefit
Now caters for >75% of the benefits administered by the government
First release 2006, still releasing monthly
100s of developers over that time, 3 vendors
multiple projects ongoing at any given time
Current fashion is towards "microservices" but monoliths are perfectly maintainable if well built and evolved.
Genuine modularity and dependency management is key
Many types of coupling (message, data, control, …)
Ensure high cohesion in modules, avoid "buckets of code"
Need to achieve measurement and visibility
Easier when code is partitioned - how does Isis do it?
The framework uses Apache Maven modules to manage compile-time dependencies
factor out generic technical/business modules
Each module with entities maps to its own (DB) schema
Foreign keys within schemas
Various techniques/features to decouple
eg good ole' dependency inversion principle
eg "table-of-two halves" pattern for polymorphic associations between entities in different modules
Technical modules
Generic (business) sub-domains
Mixins move functionality out of domain objects
composite UI creates a coherent whole for the end-user
Domain events coordinate/veto interactions between different modules within the system
URNs, enabling polymorphic associations
enabler of the "table of two halves" pattern
@Mixin
public class ToDoItem_next {
private final ToDoItem toDoItem;
public ToDoItem_next(final ToDoItem toDoItem) { ... }
public ToDoItem $$() { ... }
}
public static class CompletedDomainEvent
extends ToDoItem.ActionDomainEvent {}
@Action(
domainEvent = CompletedDomainEvent.class
)
public ToDoItem completed() {
setComplete(true);
}
@DomainService
public class ItemCompletedSubscriber {
@Subscribe
public void on(ToDoItem.CompletedDomainEvent ev) {
ToDoItem item = ev.getSource();
switch(ev.getEventPhase()) {
case HIDE:
ev.hide();
break;
}
}
}
@DomainService
public class ItemCompletedSubscriber {
@Subscribe
public void on(ToDoItem.CompletedDomainEvent ev) {
ToDoItem item = ev.getSource();
switch(ev.getEventPhase()) {
case DISABLE:
ev.disable(...);
break;
}
}
}
@DomainService
public class ItemCompletedSubscriber {
@Subscribe
public void on(ToDoItem.CompletedDomainEvent ev) {
ToDoItem item = ev.getSource();
switch(ev.getEventPhase()) {
case VALIDATE:
ev.invalidate(...);
break;
}
}
}
@DomainService
public class ItemCompletedSubscriber {
@Subscribe
public void on(ToDoItem.CompletedDomainEvent ev) {
ToDoItem item = ev.getSource();
switch(ev.getEventPhase()) {
case HIDE:
break;
case DISABLE:
break;
case VALIDATE:
break;
case EXECUTING:
break;
case EXECUTED:
break;
}
}
}
A framework generated UI allows for change. How does this work?
Replace the UI technology in use without affecting core code?
Provide more than one UI simultaneously?
What does Apache Isis assume about the UI technology it uses?
Generic UIs basically iterate over the metamodel
Extensible via "facets" (extension object pattern)
Can run multiple UIs in parallel, eg Wicket and REST
The programming model aims to generify UI concepts
eg @DomainObject(bounded=true)
⇒ a drop-down
We will develop other UIs in the future, eg Vaadin, Polymer
It is viable to write your own generic UI
Generic UIs aren’t appropriate in all cases
The Wicket viewer can be customized heavily
chain of responsibliity pattern to identify each widget
Intend to integrate with NoWicket framework
More generally, use the REST API to build a custom UI
Frameworks imply high commitment - what is being coupled to Apache Isis like?
Constraints on the domain model code?
Degree of dependence domain code on Apache Isis?
Reuse Apache Isis domain models?
Replace 3rd party components? (e.g. ORM library)
Domain objects have a compile-time coupling to the "applib" (for annotations), that’s about all
Could deploy an Apache Isis app on Spring, for example
… or any other framework providing runtime support for dependency injection and an ORM
Is dependent on DataNucleus ORM (JDO and JPA)
RDBMS and NoSQL, extensible via its StoreManager SPI
Could ignore, just use view models rather than entities
Apache Isis is an opinionated framework
One opinion we hold is there is too much emphasis on technical concerns (and not enough on the domain)
Another: the UI is a fashion item; it doesn’t make sense to try to infer the domain from the outside-in
start at the domain model, work your way out
A feedback loop is the most important thing
with testability a close second
we set up test data for prototyping using a mini-framework called "fixture scripts"
we also have another small testing framework that emulates the UI
reuse the same fixtures as used in prototyping
A fixture script create data for the test scenario:
DemoFixture fs;
Contact contact;
@Before
public void setUp() throws Exception {
this.fs = new DemoFixture();
fixtureScripts.runFixtureScript(fs, null);
this.contact = fs.getContacts().get(0);
}
The WrapperFactory
"wraps" each domain object, enforces the rules of the generic UI:
@Test
public void name_already_in_use_by_contact() throws Exception {
// given
final String existingName =
fs.getContacts().get(1).getName();
// expect
thrown.expect(InvalidException.class);
thrown.expectMessage("Reason: " +
"This name is already in use by another contact");
// when
wrap(this.contact).edit(existingName, null, null, null);
}