A model expressed in software
- Associations between objects are simple to conceive and draw, but harder to implement
- Distinctions among three patterns of model elements which express the model
- Entities
- Value objects
- Services
Associations
- For every traversable association in the model there is a mechanism in the software with the same properties
- A model that shows an association between a customer and a sales representative represents two things
- Abstracts a relationship deemed relevant between two people
- Corresponds to an object pointer between two java objects (or some other implementation)
- e..g a one-to-many association might be implemented as a collection in an instance variable
- might not be this direct, no collection, accessor queries a db
- Design has to specify a traversal mechanism
- In real life lots of bidirectional many-to-many relationships
- Tends to be true of early forms of a model as we brainstorm
- Complicate implementation and maintenance
- Communicate little about the relationship
- Arrows in both directions look the same
- Both objects can only be understood together
- At least 3 ways of making more tractable
- Impose a direction
- Add a qualifier, reducing multiplicity
- Eliminating nonsensical associations
- We seldom start with the name George Washington and ask which country he was president of
- On direction more important country -> president
- Keeps person independent of president
- Deeper understanding leads to a qualified rule
- Only one president at a time
- country -period> president
- Constraining associations makes them more communicative and simple to implement
- Significance to bidirectional
- Brokerage account, investment
- multiple brokerage accounts with multiple investments
- Only one investment per stock - add constraint
Entities
- Mistaken identity in software system leads to data corruption and data entries
- Many things are defined by identity and not an attribute
- A person has an identity starting at birth and stretching beyond death
- Persons attributes change and eventually disappear
- User could be used across multiple systems with different representations
- Still need to identify
- Identity has to be matched between multiple implementations of the objects, its stored forms, and real-world actors
- Attributes may not match
- Two customers may have the same name
- May have entered an address update in one system which is still being propagated
- Entity is a thread of identity which runs through time and across distinct representations
- Could be anything, person, city, cat, lottery ticket, bank transaction
- Not all objects in the morel are entities with meaningful identities
- Common for identity to be significant outside software system e.g. check number
- Not always
- When an object is distinguished by identity make this primary to the definition in the model
- Keep class definition simple and focused on life cycle continuity and identity
- Define a means of distinguishing each object regardless of form or history
- Define an operation that is guaranteed to produce a unique result for each object - e.g. attaching a unique symbol
- Model must define what it means to be the same thing
- Application for booking seats in a stadium may treat seats and attendees as entities
- Assigned seating: each ticket has a seat number, identity
- General admission: No need to distinguish seats
- Even if they have numbers written on them, irrelevant to model
- Error to make them entities
Modeling entities
- Best if kept spare
- Reduce to most intrinsic characteristics used to identify, or commonly used to find and match
- Only behaviour that is essential to the concepts and attributes required by that behaviour
- Remove behaviour and attributes into other objects associated with the entity
- Some will be other entities, other value objects
- Coordinate the operations of objects they own
- CustomerID is only identifier of customer, but often queried by name, phone, address
- May include in the entity
- Identity operation
- May create object multiple times - loaded from db
- Could have multiple versions - updates propagating through distributed db
- Can try to make a unique key from combination of attributes that can be constrained to be unique
- Name changes, need to be consistent over life cycle
- Attach each a unique symbol
- Designated immutable
- ID may not need to be seen by user
- But might in some cases - tracking number for package
- May need to be unique outside the system's boundaries
- Medical records exchanged between hospitals
- May use an identifier from another institution like a government agency
- Not foolproof
- If ID or operations aren't a meaningful distinction in the domain it confuses matters more
- Identity assigning operations often involve human input
- May offer likely matches and leave it to the user to confirm
Value objects
- Natural to consider assigning identity to everything
- System has to cope with all that tracking
- Rules out performance optimizations
- Effort is required to define meaningful identities and work out foolproof ways to track across distributed systems
- Muddles the model, making all objects look the same
- Not just the absence of identity
- describe things
- descriptive aspect of the domain
- Only care about what they are, not who they are
- Colours, strings, numbers, amounts
- Can be an assemblage of other objects
- house plan, window style
- Can reference entities
- Route could reference roads, cities
- Often passed as parameters in messages between objects
- Frequently transient, created for an operation and discarded
- Attributes of identities, person is an entity, but name is a value
- Treat value objects as immutable
- Avoid design complexity needed to maintain entities
- Should form a cohesive whole, Address is a street, city, state
Designing value objects
- Don't care which instance of a value object we have
- Design freedom to simplify design or increase performance
- Need to make choices about copying, sharing and immutability
- Name object is interchangeable, can copy between people
- Two person objects may not need their own name instances
- Pointer to same object
- Changing it for one person would change it for the other
- Make immutable
- Same issue when passing attribute to another object
- Immutable or make copy
- Extra objects for performance tuning can be important - lots of value objects
- If each electrical outlet in a house plan is an entity, could be hundreds in one plan
- Make interchangeable value objects
- flyweight
- Copying vs sharing
- Copying can clog the system, but sharing can slow down a distributed system - locking
- Sharing better left to cases where it is most valuable and least troublesome
- Saving space or object count in db is critical
- communication overhead is low
- shared object is strictly immutable
- Mutable objects can be freely shared
- When to allow mutability
- If it changes frequently
- If object creation or deletion is expensive
- If replacement will disturb clustering
- If mutable it must not be shared
Tuning a db with value objects
- db places data on a physical disk
- Takes time for physical parts to move around and read
- Attempt to cluster physical addresses so that related data can be fetched in a single operation
- If referenced by many objects, will not be located near most of them
- Making a copy allows it to be stored on the same page as each entity - denormalization
- Also storing the value object on another server requires sending a request to fetch
Designing associations which involve value objects
- Bidirectional associations between value objects make no sense
- Meaningless without identity
- If it seems like you need a bidirectional association one or both should probably be an entity with an identity that hasn't been recognized yet
Services
- Operations which don't fit on a single object
- Forcing the behaviour on an object loses it's conceptual clarity and becomes hard to understand
- These operations usually draw together many domain objects
- Many responsibilities
- Sometimes end up as a domain object with the name Manager
- No state or meaning beyond operation they host
- Some concepts aren't natural to model as objects
- Service is an operation offered as an interface that stands alone in the model without any state
- Named for an activity instead of an entity - verb, not noun
- Still have defined responsibility and interface as part of the domain model
- Operation names should be from ubiquitous language
- Don't strip all behaviour off entities and domain objects
- Three characteristics for a good service
- Operation related to a domain concept that is not naturally a part of an entity or value object
- Interface is defined in terms of other elements of the domain model
- Operation is stateless
- Any client can use any instance without regard to its history
- Execution will use state available globally and possibly have side effects
- Does not hold its own state which affects its behaviour
Services and the Domain Layer
- Most services discussed in the literature are part of the infrastructure layer
- General design pattern
- Need to distinguish
- Bank send emails when a customer's balance drops below a certain amount
- Infrastructure service: from email program
- Application layer: orders a notification
- Domain layer: determines if the account falls below the threshold
- Might noe be a service, more appropriate for the account entity
- If a service was created to transfer funds it would belong in the domain layer
- Technical infrastructure services lack any business meaning
- Services act like scripts that organize the potential of the domain to get something done
- Entities and value objects are often too fine grained to provide convenient access to the domain layer
- Fine line between domain and application
- Importing and exporting transactions to spreadsheet - application, no meaning of file formats in the domain of banking
- Transferring between accounts encodes significant business rules
- Awkward to put on accounts since it interacts with multiple as well as global business rules
- TODO
Granularity
- Decoupling clients from entities and value objects
- Medium grained services can be easier to reuse, encapsulate significant functionality into a simple interface
- Fine grained objects can lead to knowledge leaks from the domain layer into the application layer
Access to services
- Means of providing access is not as important as the design decision to carve off specific responsibilities
- A doer object may be satisfactory
- A simple singleton could provide access
- Coding conventions can make it obvious that these are just delivery mechanisms for service interfaces
- Only use elaborate architectures when necessary e.g. to distribute the system
Modules
- Give two views of the model
- Can look at detail in a module without being overwhelmed
- Can look at relationships between models without interior detail
- Should emerge as a meaningful part of the model
- Not just code being divided into modules - concepts
- Low coupling between modules, high cohesion within
- Particularly important for larger scale design
- When two elements are separated into modules relationship becomes less direct
- Increases overhead of understanding
- low coupling between modules reduces that cost
- High cohesion within a module allows work to concentrate within a module
- Should coevolve with smaller elements
- Typically chosen to organize an early form of objects
- But then they tend to change in ways that keep them part of a particular module
- Refactoring modules is more work than classes
- But letting modules reflect changing domain allows objects within them to evolve
- Communication mechanism - meaning of the objects being partitioned needs to drive the choice of modules
- Telling developers to think about them together
- Module names enter ubiquitous language
Agile modules
- Changing modules requires widespread updates to the code
- Can be disruptive to team communication and cause problems with tools e.g. source control
- Modules typically reflect an earlier form of the model than classes
- Inevitable early mistakes lead to high coupling between modules
- Makes refactoring harder, makes problem worse
- Need to bite the bullet
- Look for ways to minimize the work of refactoring modules
- Java requires import of specific classes, even though modeler thinks of packages as depending on other packages
- Still need to import into specific classes
- But can import an entire package
Infrastructure driven packaging
- Tiered architectures can fragment the implementation of model objects
- Some frameworks cause this by spreading the responsibilities of a single object over multiple objects and placing them in separate packages
- e.g. J2EE data and data access into entity bean, business logic into session bean
- Often separated into different packages
- Lowers cohesion
- Each conceptual object broken into 4 tiers
- data persistence
- behaviour intrinsic to object in all situations
- application specific functionality
- decoupled public interface
- bit too complicated, but each was well defined
- tidiness to separation of concerns
- Helped at times - moving data persistence removed clutter
- But framework required separate packages with a particular naming convention
- Developers tended to avoid making too many modules (multiplied by 4)
- Too hard to refactor
- Finding all data and behaviour that made up a single object was too hard - combined with indirectness of layering
- Won't go into solutions - book is not on framework design
- But not compromising the model is more important than this separation
- Infrastructure packaging schemes impose two costs
- if conventions pull apart domain elements then the code no longer reveals the model
- Only so much partitioning a mind can stitch back together
- If framework uses it all up, none is left for the domain
- Keep things simple
- Unless need to distribute code keep all code that implements a single conceptual object in the same module if not object
- Use packaging to separate domain layer
- Leave as much freedom as possible to domain developers to package in a way that supports the model
- Generated code is better to put into a separate package so it doesn't clutter the elements developers actually need to work with
Modelling paradigms
- Entities, value objects, services, modules provide building blocks for an object model
- Model driven design oes not mean forcing everything into an object mold
- There are other paradigms such as rules engines
- Need to make pragmatic tradeoffs
- means to the end of model driven design, not alternatives
- Dominant paradigm is object oriented design
Why object paradigm predominates
- Balance of simplicity and sophistication
- Too esoteric -> not enough developers will master it
- Rich enough to capture important domain knowledge
- Circumstantial advantages
- maturity, widespread adoption
- mature infrastructure
- tool support
- most integration problems solved for objects
- Novel paradigm may be unable to find developers
- Example
- Project used an OO database which was new at the time
- Gradually realized that the test data was using a significant portion of the DBs capacity
- Production would have a lot more data
- Production would have a higher transaction volume
- Brought in one of the few people who understood the new technology
- Three problems
- Off the shelf infrastructure provided with the DB didn't scale to their needs
- Storage of fine grained objects is more costly than they realized
- Parts of the object model had a tangle of interdependencies that contention was a problem with a small number of concurrent transactions
- With expert help replaced the off the shelf infrastructure
- Found models that worked better with the technology instead of storing lots of fine grained objects
- Deepened thinking about limiting the web of relationships in the model and decoupling
- lots several months in this recover - in addition to earlier months going down the failed path
- Now OO technology is relatively mature
- Common infrastructure works with off the shelf solutions
- Critical tools come from major vendors or stable open source projects
- infrastructure pieces are widely used with documentation, books, and people who understand them
- Other paradigms don't have this maturity
- Won't be locked into object based systems because object bases systems are widely used and integration tools are likely available
- Domains that aren't natural to model as objects
- intensely mathematically
- dominated by global logical reasoning
Nonobjects in an object world
-
Domain model doesn't have to be object based - there are model driven designs written in prolog
- Model made up of logical rules and facts
-
Model paradigms are conceived to address ways people think about domains
-
Models are shaped by the paradigm
-
There are likely to be parts of the domain that are better expressed in a paradigm other than the dominant one
- When there's just a few developers can live with a few awkward objects
- When different major parts of the domain it's appealing to use a mix of tools and paradigms
- When the interdependence is small a subsystem in the other paradigm can be encapsulated
- e.g. complex math calculation called by an object
- Other times more intertwined
- e.g. Interactions of objects depends on mathematical relationships
- Motivates integration into object systems of non object components such as business rules engines and workflow engines.
- Most systems must use non object infrastructure like rdbms
Sticking with model driven design when mixing paradigms
-
Rules engines are common
- A knowledge rich domain model probably contains explicit rules
-
Object paradigm lacks specific semantics for stating rules and their interactions
-
Rules can be modelled as objects
- But encapsulation makes it apply global rules across the whole system
-
Rules engine technology is appealing because it promises to provide a more natural and declarative way to define rules
-
Logic paradigm is well developed and powerful - seems like a good complement to strengths and weaknesses of objects
-
Doesn't always work out
- Some products just don't work well
- Some lack a seamless view that can show the relatedness of model concepts between the two implementation environments
- Common outcome is program fractured in two
- static data storage system using objects
- ad hoc rules processing application that's lost all connection to the object model
-
Need to continue thinking in terms of models while working with rules
-
Need to find a single model that works with both paradigms
- Not easy but should be possible is the rules engine allows an expressive implementation
- Otherwise data and rules become unconnected
-
Most effective tool for holding the parts together is a robust ubiquitous language
- Consistently applying names across the two paradigms
-
While model driven design does not have to be object oriented it does depend on having an expressive implementation of the model constructs
- Unexpressive implementation negates the advantage of an extra paradigm
-
Rules of thumb
- Don't fight the implementation paradigm
- There's always another way to think about the domain
- Find model concepts that fit the paradigm
- Lean on the ubiquitous language
- Don't get hung up on UML
- Focus on UML can lead people to distort the model to what can be easily drawn
- e.g. UML fetuses for representing constraints are not always sufficient
- Other types or drawing or english descriptions can be better than trying to force into UML
- Be skeptical
- Is a tool really pulling its weight
- Just because you have some rules, doesn't necessarily mean you need the overhead of an entire rules engine
- Rules can be expressed as objects
-
Should exhaust the options of the dominant paradigm before trying to mix
-
Relational paradigm is a special case