Apache Isis - Closing the Architecture/Code Gap

Dan Haywood & Eoin Woods

About Us

  • 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.

Problem of Successful Systems

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.

Specific Problems

  • 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

Apache Isis (and naked objects)

  • 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

110
120
130
140
150
160

A metaphor: the incredible machine

incredible machine

Generic UI/UX

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

Generic UIs are "good enough" for many applications

  • …​ though it has, admittedly, been a journey to get them to that level!

2003 dnd
2005 dsfa
2007 rcp
2009 scimpi
2013 TransportDemand
2014 estatio
2015 commplan
2016 todoapp

An example: Estatio

  • 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

Customizing the UI

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?

Customizing the UI

  • 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.

Other Cross-cutting Concerns

  • 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

swagger

Enterprise Architecture

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?

Bounded contexts, standard interactions

  • 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

Inferred commands

  • 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)

410
420
430
440
450

A published action

@Action(
    invokeOn = InvokeOn.OBJECT_AND_COLLECTION,
    publishing = Publishing.ENABLED
)
public Invoice approve() {
    setStatus(InvoiceStatus.APPROVED);
    return this;
}

PublisherService

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>

Concluding

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

Example Apps

Learning More

Appendix: Architectural Constraints

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

150
180

Existence proof

  • 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

Appendix: Maintainable Monoliths

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?

Modularity approach

  • 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

310
320

Pre-built modules

Modularity features of Apache Isis

  • 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

Mixins

2016 todoapp with mixins

Mixins

@Mixin
public class ToDoItem_next {
    private final ToDoItem toDoItem;
    public ToDoItem_next(final ToDoItem toDoItem) { ... }

    public ToDoItem $$() { ... }
}

Domain events

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;
        }
    }
}

Appendix: Alternative UIs

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?

Alternative Generic UIs

  • 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

GES Consultor

gesconsultor

User Archetypes

Generic UIs aren’t appropriate in all cases

210

Alternative Custom UIs

  • 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

swagger

Appendix: Coupling to the Framework?

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)

Decoupling from the framework

  • 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

Beliefs/prejudices

  • 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

Support for Testing

  • 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);
}