ted.neward@newardassociates.com | Blog: http://blogs.newardassociates.com | Github: tedneward | LinkedIn: tedneward
Smalltalk is...
object-oriented
dynamically-typed
message-passing
reflective
... programming language
... and environment
Current Smalltalk implementations
Amber (runs in Javascript browsers!)
http://www.amber-lang.net/
Squeak
https://en.wikipedia.org/wiki/Squeak
GNU Smalltalk
https://en.wikipedia.org/wiki/GNU_Smalltalk
Concomm Smalltalk (ObjectStudio, VisualWorks)
http://www.cincomsmalltalk.com/main/
Pharo
http://pharo.org/
Smalltalk's history is long and distinguished
began development in 1969
developed at Xerox PARC
invented by Alan Kay (with help from Dan Ingalls)
influenced by Lisp, Simula, Logo, Sketchpad
first release -- Smalltalk-71
first public release -- Smalltalk-80
Smalltalk influenced...
AppleScript
Common Lisp Object System
Dart
Dylan
Erlang
Etoys
Falcon
Smalltalk influenced...
Go
Groovy
Io
Ioke
Java
Lasso
Lisaac
Smalltalk influenced...
Logtalk
NewtonScript
Object REXX
Objective-C
PHP 5
Perl 6
Smalltalk influenced...
Python
Ruby
Scala
Scratch
Self
Began on a bet
can a programming language based on the idea of message passing inspired by Simula be implemented in 'a page of code'?
incidentally, Kay answered that in "a few mornings"
Objects introduced in Smalltalk-80
Smalltalk object can do three things:
hold state (references to other objects)
receive a message from itself or another object
in the course of processing a message, send a message to itself or another object
no difference between values which are objects and primitive types
"in Smalltalk everything is an object"
classes are also instances of objects
Smalltalk incorporated IDE and runtime into one tool
this is the "Smalltalk browser"
objects were visual objects
manipulate the objects to manipulate the environment
image files were of the entire runtime
MVC is a design pattern that appears frequently in GUI systems
Model represents the state
Controller represents the logic
View represents the display/UI
Origins lie in Smalltalk
Models were objects
Controllers were objects
Views were objects
Today, this is an idiom known as "Naked Objects"
MVC showed up in a number of GUI systems
Windows UI frameworks called it "Document/View"
Java Swing built around MVC quite deeply
tables, lists, all used models extensively
even the actual UI code was split along view/controller lines; AbstractButton defined basic button behavior, and deferred to another class to do the actual pixel-drawing work
iOS uses it (ViewControllers are controllers)
In the original MVC...
Models were often connected to more than one View
Controllers coordinated view updates as model changed
Models, Views and Controllers relatively loosely coupled
Web adaptation of MVC changed this to a 1:1 View/Controller
Web views and controllers never reused
Parting thoughts
Models are not necessarily Domain Objects
community is split over this one
Views should not contain "logic"
except for logic that is UI-focused
community is split over this one too
MVC has spawned a lot of similar ideas/patterns
Hierarchical model-view-controller
Model-view-adapter
Model-view-presenter
Model View ViewModel
Observer
Presentation-abstraction-control
Three- or n-tier architecture
Resources
For more details, see
http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
Observer pattern comes from "Design Patterns" (GOF book)
Gamma, Helm, Johnson, Vlissides
Presentation-abstraction-control comes from "POSA 1"
Buschmann, Meuneir, Rohnert, Sommerlad, Stal
In the old days...
objects were intended to be directly user-manipulable things
data directly modified/changed
methods directly invoked
connections directly displayed/modified
... but we have since lost that simple idea
What's wrong with the way we do applications today?
too much complexity in the UI code
too many opportunities to get the business rules wrong
too much time spent building the system
too much effort required to understand/maintain it
New idea: Let the UI center around the objects
"All business logic should be encapsulated onto the domain objects."
"The user interface should be a direct representation of the domain objects, with all user actions explicitly consist in the creating or the retrieving of domain objects and/or invoking methods on those objects."
"The user interface shall be 100% automatically created from the definition of the domain objects."
Benefits:
faster development cycle
greater agility
more user-centric/empowered user interface
easier requirements analysis
Naked Objects frameworks core concepts
Domain Objects
properties
collections
actions
Value types
Non-objects
Factories/Repositories
services
external/system services
ViewModels
Domain objects
domain objects represent the persistent entities described within the system
these are your "plain old objects" (POJOs, POCOs, etc)
typically they intend to represent state plus behavior
state is expressed as properties
behavior is expressed as actions
Properties
represents an element of state in a domain object
typically one of three types
value property (property of value type)
reference property (association to another domain object)
collection property (collection of properties)
properties can have some UI logic or behavior attached to them
control the order in which properties are displayed
trigger behavior when a property is changed
input validation
Actions
actions are methods intended for user invocation
typically these are "just methods" that are discovered
Value types
these are basically non-referenced objects
we care only for their value
they lack a standalone "identity"
embedded "in-place" for storage
Services
services are code that aren't attached specifically to objects
these are often pure logic/behavior/transformational in nature
NakedObject services come in three flavors:
creating/retrieving domain objects (factories/repositories)
provide a bridge to external functionality (external services)
provide functionality shared by multiple domain objects which do not share a common superclass (contributed actions)
Factories/respositories
factory is a service for manufacturing new instances of an object
repository is a service for obtaining existing instances of objects
whether these are the same service is a point of debate
NakedObjects frameworks typically support either style
most often, objects will be found by navigating from other objects
Pet -> Owner doesn't require the use of a repository, for example
External services
bridge to external functionality
such as accessing an email system to send/receive messages
these will vary in size and shape with the external functionality being accessed/used
look to avoid duplicating Naked Object functionality here!
shouldn't need to write a database access service, for example
Contributed actions
a contributed action is an action that is defined on a service but which is presented to the user as an action on an individual domain object (or collection of objects)
these are, effectively, "aspect-oriented" actions applied to Naked Objects
Apache Isis
top-level apache.org project
provides a NakedObjects implementation as Web app
uses HSQL as built-in database by default
uses Apche Shiro for auth/auth
Bootstrap the Maven archetype
Java latest
Maven latest
Build an empty Isis application
mvn archetype:generate \ -D archetypeGroupId=org.apache.isis.archetype \ -D archetypeArtifactId=simpleapp-archetype \ -D archetypeVersion=1.15.0 \ -D groupId=com.mycompany \ -D artifactId=myapp \ -D version=1.0-SNAPSHOT \ -D archetypeRepository=http://repository-estatio.forge.cloudbees.com/snapshot/ \ -B
Build and run the app
$ cd myapp $ mvn clean install $ mvn -pl webapp jetty:run
Browse to http://localhost:8080 (login w/ "sven"/"pass")
Start with "simple" O-O domain class
identifiable and unique
natural sort order
implements Comparable
containing (some) business logic
validating rules
... and so on
Isis uses Java annotations to carry metadata
identification of domain entities
presentation customization
persistence customization
... and so on
@DomainObject
identifies that this class is an Isis domain object
annotation data elements control details
editing: whether the instance can be considered immutable
Domain Objects
@DomainObjectLayout() @DomainObject(auditing = Auditing.ENABLED) public class Convention implements Comparable<Convention> {
Properties
typical private-field-public-getter-setter idiom
presumption is that getter and setter "do the right thing"
or Project Lombok Getter/Setter annotations can generate methods
some properties (objects that are value objects) should be @Persistent-annotated
@Property
annotation customizes property details
editing: whether the instance can be considered immutable
objects can still be modified, just no through the UI
maxLength: maximum length allowed
hidden: indicates where in the UI this property should be hidden
EVERYWHERE, OBJECT_FORMS, PARENTED_TABLES, STANDALONE_TABLES, ALL_TABLES, NOWHERE
regexPattern/regexPatternFlags: regular expression for validation
Domain Objects
@javax.jdo.annotations.Column(allowsNull = "false", length = 40) @lombok.NonNull @Property(editing = Editing.DISABLED) @Title(prepend = "Convention: ") @MemberOrder(sequence="1") private String name; public String getName() { return this.name; } public void setName(String val) { this.name = val; }
Domain Objects
@javax.jdo.annotations.Persistent @javax.jdo.annotations.Column(allowsNull = "true") @Property(editing = Editing.ENABLED) @MemberOrder(name="Dates", sequence="1") private LocalDate start; public LocalDate getStart() { return this.start; } public void setStart(LocalDate val) { this.start = val; // calculate some reasonable values for the other dates } public String validateStart(LocalDate proposed) { // validate the start date is in the future (from now) return null; }
Properties can be validated
validate
{propertyName}()
methods
returns a String
String contents contain message for validation failure
or return null for acceptance
see "Classes, Methods, Schema" documentation for full list
Actions
typical public method
presumption is that the method does not represent a property or collection
actions often used to allow for restricted editing
actions also often create objects from menu options
@Action
annotation used to customize actions
restrictTo: whether the action is available in production or just prototyping
NO_RESTRICTIONS,PROTOTYPING
semantics: provide UI hints about the action's semantics
SAFE_AND_REQUEST_CACHEABLE, SAFE, IDEMPOTENT, IDEMPOTENT_ARE_YOU_SURE, NON_IDEMPOTENT, NON_IDEMPOTENT_ARE_YOU_SURE
typeOf: type of objects returned from a collection
as a fallback
Domain Objects
@Action(semantics = SemanticsOf.NON_IDEMPOTENT_ARE_YOU_SURE) public void delete() { final String title = titleService.titleOf(this); messageService.informUser(String.format("'%s' deleted", title)); repositoryService.removeAndFlush(this); }
Dependent services are injected
use the javax.inject.Inject
annotation to annotate the field
typically the domain object's repository is injected
other services as need demands
Domain Objects
@javax.inject.Inject @lombok.Getter(AccessLevel.NONE) @lombok.Setter(AccessLevel.NONE) EventRepository eventRepo; @javax.inject.Inject @lombok.Getter(AccessLevel.NONE) @lombok.Setter(AccessLevel.NONE) RepositoryService repositoryService; @javax.inject.Inject @lombok.Getter(AccessLevel.NONE) @lombok.Setter(AccessLevel.NONE) MessageService messageService; @javax.inject.Inject @lombok.Getter(AccessLevel.NONE) @lombok.Setter(AccessLevel.NONE) TitleService titleService;
Presentation is derived from class
@DomainObjectLayout
annotation can control UI aspects
@MemberOrder
controls the order of appearance of properties in the table
name defines grouping ("General" by default), sequence defines order
@MemberGroupLayout
controls the layout of groups of members
object menus derived from actions present on the object
More sophisticated layout can be done via a DomainObject.layout.xml file
initial versions of these can be downloaded from the prototyping UI
Presentation "hints" can come elsewhere
title()
method returns full object title
or use @Title
to mark which properties provide title
choices
{name}()
method(s) returns list of UI choices
autoComplete
{name}()
provides auto-complete UI behavior
disable
{name}()
determines whether to disable UI element
hide
{name}()
determines whether to hide UI element
Presentation "hints" can come elsewhere
cssClass()
returns CSS class to use
@ActionLayout()
, @PropertyLayout
or @CollectionLayout
can specify CSS to use per-element
iconName()
returns icon (file) name to use
Object lifecycle
transient v persistent
objects begin as transient until persisted
create objects using container.newTransientInstance(T.class)
programmatically save using container.persist()
once saved, objects save themselves henceforth
Persistence is handled by JDO
using the DataNucleus JDO classfile enhancer
uses JDO annotations to control schema and queries
does bytecode enhancement at compile-time
by default, runs in-memory H2 database
obviously can be configured to use any JDBC database
JDO class annotations
@PersistenceCapable
: the class can/should be stored
identityType: How do we want to track identity?
schema: What schema does this type belong in?
@DatastoreIdentity
: what's the strategy we want to use for OIDs?
strategy: the strategy (enumeration) to use
column: the column in which to store the identity
IdentityType.DATASTORE and "id" are the usual values here
@Version
: how do we want to track instance differences?
strategy: the strategy (enumeration) to use
column: the column in which to track the differences
VersionStrategy.DATE_TIME and "version" are the usual values here
JDO class annotations
@Queries
: defining queries that can be invoked
this is an array of Query annotations
each Query annotation has a "name" and "value" (being the query text)
this is JDOQL, not SQL (which are very similar in syntax)
@Unique
: defining uniqueness constraints on the class/table
Domain Objects
@javax.jdo.annotations.PersistenceCapable( identityType = IdentityType.DATASTORE, schema = "dragonflight" ) @javax.jdo.annotations.DatastoreIdentity( strategy = IdGeneratorStrategy.IDENTITY, column = "id") @javax.jdo.annotations.Version( strategy= VersionStrategy.DATE_TIME, column ="version") @javax.jdo.annotations.Queries({ @javax.jdo.annotations.Query( name = "findByName", value = "SELECT " + "FROM domainapp.dom.impl.Convention " + "WHERE name.indexOf(:name) >= 0 ") }) @javax.jdo.annotations.Unique(name="Convention_name_UNQ", members = {"name"}) // Should have a query that finds the upcoming one (by date)
JDO field annotations
@Column
: describes the column-storage details for this field
allowsNull (boolean): can we store without a value here?
length (int): storage size of the column (characters)
@Persistent
: indication that the non-primitive field should be stored as a "dependent" value
meaning, a value object with no identity of its own
Object lifecycle methods
created()
called when an object has just been created using newTransientInstance()
loaded()
called when a (persistent) object has just been loaded from the object store.
persisted()
called when object has just been persisted from the object store.
persisting()
called when a (not-yet-persistent) object is just about to be persisted from the object store
Object lifecycle methods
removed()
called when a (persistent) object has just been deleted from the object store
removing()
called when a (persistent) object is just about to be deleted from the object store
updated()
called when a (persistent) object has just been updated in the object store
updating()
called when a (persistent) object is just about to be updated in the object store
Services are "just" domain objects
they represent behaviors, not state
they frequently provide entrypoints into the system
such as creating objects
or they provide access outside of the system
Isis-provided services
ClockService
ConfigurationService
MessageService
RepositoryService
ServiceRegistry
TitleService
UserService
Isis-provided services
EventBusService
EmailService
AuditerService
BackgroundService
... and a lot more
Despite that, you will want to write your own services
repository services
for creating and finding domain entities
shared behavior services
behavior that should be shared across non-related domain entities
"external access" services
behavior to access external systems not already provided by Isis
Services are "just" domain objects
@DomainService
annotation helps discoverability
nature parameter describes the "visibility" of this service
VIEW_MENU_ONLY
: only appear in the GUI
VIEW_REST_ONLY
: only appear in the Restful Objects viewer
DOMAIN
: only for programmatic use; don't show in UI
Presentation
By default each domain service corresponds to a single menu item on the menu bar
@DomainServiceLayout
can control the grouping and layout
"primary menu" is the leftmost grouping in the top menubar
"secondary menu" is the rightmost grouping in the top menubar
"tertiary menu" is a menu under the user's name in the upper-right corner
Reading
Richard Pawson's PhD thesis
http://downloads.nakedobjects.net/resources/Pawson%20thesis.pdf
"Naked Objects"
http://www.nakedobjects.org/book/
"DDD Using Naked Objects", by Dan Haywood (Pragmatic Bookshelf)
http://www.pragprog.com/titles/dhnako/domain-driven-design-using-naked-objects
Architect, Engineering Manager/Leader, "force multiplier"
http://www.newardassociates.com
http://blogs.newardassociates.com
Sr Distinguished Engineer, Capital One
Educative (http://educative.io) Author
Performance Management for Engineering Managers
Books
Developer Relations Activity Patterns (w/Woodruff, et al; APress, forthcoming)
Professional F# 2.0 (w/Erickson, et al; Wrox, 2010)
Effective Enterprise Java (Addison-Wesley, 2004)
SSCLI Essentials (w/Stutz, et al; OReilly, 2003)
Server-Based Java Programming (Manning, 2000)