BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Rapid Application Development using Naked Objects for .NET

Rapid Application Development using Naked Objects for .NET

This item in japanese

Bookmarks

Naked Objects for .NET is a framework that provides an implementation of the naked objects architectural pattern for the .NET framework. The basic concept behind naked objects is that, when writing a business application, the only thing you should write are the domain objects and the business logic should be encapsulated in those domain objects. A framework then exposes the domain objects to the user in the form of a rich object-oriented style of user interface, and also takes care of the persistence and management of those objects, typically via an ORM. This pattern is likely to appeal most strongly to those who subscribe to the idea of domain-driven design. Apart from eliminating the need to write a user interface layer and data access layer, the naked objects pattern also facilitates good object modelling - because you can instantly turn a prototype domain model into an application that can be evaluated by business users.

Most people’s reaction when they hear this is that it can’t possibly work for large-scale, complex business applications. Yet more than 1000 officers of the DSFA, a Department of the Irish Government, use applications written for Naked Objects to support the administration of social welfare benefits worth billions of euro annually.

History

The Naked Objects framework started its life on the Java platform. The earliest versions were written in Java 1.1. In what has to be one of the greatest flukes in the history of software development, we discovered that it would run, unmodified, on the .NET platform as J#. However, as later versions of Naked Objects migrated to Java 1.5 the .NET capability was lost. The final stroke was in January 2007 when Microsoft confirmed that J# would not be supported from .NET 3.5 onwards.

To address this, Naked Objects for .NET was created as a complete reimplementation of the framework. Written in C#, it is designed to take full advantage of the .NET platform capabilities including generics and LINQ. The generic user interface created by Naked Objects for .NET is written in WPF.

How Naked Objects works

This generic user interface does not rely on code-generation: it is created dynamically. At run-time, the framework uses reflection in order to render your domain objects on the user interface. This technique, known as introspection, is what allows developers to quickly go from domain model to usable application. This screenshot shows an open view of an object, which itself contains links to several associated objects (each icon represents a domain object); the user may navigate to those associated objects just by clicking:

(All the screenshots and code samples shown in this article are taken from a simple Expenses Processing application, which is included as part of the Naked Objects download.)

There are, of course, many frameworks that can render a domain object model in the form of a simple CRUD user interface, suitable for database browsing or maintenance. What makes Naked Objects more interesting is that it can produce a fully-functional application. By default, any public method will be made available to the user in the form of an action on a pop-up menu on the object’s icon, with the method name reformatted as the action name:

If the method has parameters method signature is used to create a dialog box for invoking the action, so:

public IExpenseItem CopyAnExistingExpenseItem( IExpenseItem otherItem)

Is rendered as:

The Other Item field requires the user to drag or paste in an existing object of type IExpenseItem (the UI field will reject an object of any other type). To the right of field there is also a drop-down indicator, which automatically provides a list objects of that type that are currently visible to the user on other tabs – which avoids needless switching between tabs.

How does the user find an Expense Item that isn’t on the screen or in that drop-down list, or, for that matter, create a new object instance when they don’t have another object to start from? This is where Repositories and Factories come in - standard patterns in domain-driven design. In Naked Objects terms, Repository and Factory are both just examples of services, services are just first-class domain objects in their own right. You can make use of services in three ways, though.

First, the services provide the main menu rendered at the top of the screen . Typically these provide actions that are the start points for business activities, such as creating a new Customer, or finding an existing one. Secondly, services can be injected into any other domain object that needs them, following the dependency injection(DI) pattern. Naked Objects manages this transparently - all you need to do is provide a set-able property of the type of the service required - there is no need for external configuration as required by many DI frameworks.

The third use of services is for what we term ‘contributed actions’. If a service has a public method:

public virtual IList <RecordedAction>
	allRecordedActions(IRecordedActionContext context)

Then this will automatically be contributed as a user action to any object that implements IRecordedActionContext, with the first field of the dialog automatically filled with the object you selected the action on. (This action appears within the ‘Recorded Actions’ sub-menu shown on the first screenshot - that being the name of the service that provides the action).

This powerful capability gives you way to achieve a kind of multiple-inheritance - at least from a user perspective. It has some conceptual similarity to the concept of mix-ins or to .NET extension methods, though the implementation is somewhat different.

Implementing business rules

So how would you implement business rules? There are two approaches. The first is through the use of attributes, which may be applied at the level of classes, properties, methods or parameters. For example:

  • By default, the framework requires the user to complete all fields before being able to save an object and to provide all fields in a dialogue before being able to execute an action. You may use an [Optionally()] attribute to override that behaviour.
  • [MaxLength()], [Mask()], and [RegEx()] allow you to specify constraints on input strings and/or to format them.
  • If you have a property or method that must be public for programmatic reasons but which is not relevant to the user, then it may be [Hidden]. Various conditions may also be applied to this attribute.
  • By default, the names of classes, properties and actions will be used, with some friendly reformatting, in the user interface - which we consider to be good practice. However, if you need a label that can’t be used in the code (because it contains symbols or punctuation, for example), then the [Named()] attribute may be used. (Note that there is an entirely separate mechanism to provide internationalisation of all the labels).

The second approach, which offers far more flexibility, uses programming by convention as illustrated below:

public virtual void CopyFrom(IExpenseItem otherItem) {...}
public virtual string ValidateCopyFrom(IExpenseItem otherItem) {...}

In this example, the CopyFrom method will be rendered by the framework as a user action; the framework will also recognised the ValidateCopyFrom method as providing logic for validating the parameters of the action: if this method returns a non-null string then the ‘OK’ button on the dialog box will be greyed-out and the string message made available to the user as a tool-tip. Similarly, DisableCopyFrom would provide logic for making the action unusable , for example when a claim has already been persisted. (Note that the framework has an entirely separate mechanism for managing permissions based on a user’s roles). There are several other such conventions, for example to specify default values or specific choices (drop-down lists) for parameters.

POCOs

These coding conventions add optional behaviour. There are just three simple coding conventions that you must follow. Two apply to properties as shown here:

public virtual Employee Claimant {

    get {

        Resolve(employee);

        return employee;

    }

 

    set {

        employee = value;

        ObjectChanged();

    }

}

The Resolve() call ensures that the object has already been loaded into memory, and the ObjectChanged()notifies the framework that a property value may have changed - both in order to update any views of that object, and to ensure that the change is persisted. (Note that the framework handles this persistence for you automatically: you do not write any methods for loading, saving or updating objects).

The third requirement is that any object that you create programmatically is notified to the framework: instead of writing:

Employee employee = new Employee();

You need to write:

Employee employee = Container.NewTransientInstance< Employee>();

These calls are not made to the Naked Objects framework though, they just delegate to methods defined on an IDomainObjectContainer. If you provide a property of this type on your object, the framework will inject a container that implements these functions for you at runtime. As a short-cut, you have the option to make your domain objects inherit, ultimately, from AbstractDomainObject, which will save you a few lines of code in each case.

But it is important to stress that this inheritance is optional: Naked Objects is a POCO based approach. Apart from POCO being a good thing in general, it means that you have the option to take the same domain objects and run them in a more conventional architecture: writing a custom user interface and other layers as required. All you’d have to do is stub-out those three types of call to the Container. (If you are willing to use Aspect Oriented Programming, then you could get rid of the Resolve(), ObjectChanged()and newTransientInstance() calls from your domain code altogether - but we did not want to tie Naked Objects specifically to an AOP implementation.)

The attraction of this is that you could simply use Naked Objects to support the Domain-Driven Design parts of the process: you wouldn’t be forced to implement the system on Naked Objects. And the good news is that all you then need is the Express Edition of Naked Objects, which is a free download.

Rate this Article

Adoption
Style

BT