persistent, rich domain models • Transparent persistency, strong consistency guarantees across multiple concurrent actors • Designed for modularity • No commitment with a particular backend • Bible Chapter 6
Serializable, JSON, Enums • User-defined: Any type • Multiple aliases for the same type • Must externalise to one or more value types • MUST be immutable valueType java.util.Locale as Locale { externalizeWith { String toLanguageTag(); } internalizeWith forLanguageTag(); } ! OR ! valueType java.util.Locale as JsonLocale { externalizeWith { String getLanguage(); String getCountry(); } }
• Each role must have a name (unique for that type) • Multiplicity ranges • E.g.: 0..1, 1..*, 5..9 • Upper multiplicity determines relation type (to-one, to-many) relation SystemUsers { protected Bennu playsRole bennu { multiplicity 1..1; } protected User playsRole user { multiplicity *; } }
is modified • Useful for ensuring constraints are maintained • Allows notifications before add, after add, before remove and after remove. static { Registration. getRelationShiftStudent(). addListener( new ShiftStudentListener()); }
They are re-generated for each module, and depend on the underlying backend • Undefined behaviour if invoked outside a transactional context (Hint: NPE) • Each DomainObject has a unique External ID • Object equality determined by identity (i.e. equals 㱻 ==) • Serializable, stores the object’s ID
within a transaction • Three different types of transactions: • Read-Only - Any write will throw a WriteOnReadError • Speculative Read - Write causes restart in full write • Write • Allows listeners and introspectors • Provide transaction-local storage
Manually - Begin, Commit, Rollback • FenixFramework.getTransactionManager().withTransaction(Callable<T>) • @Atomic methods • withTransaction or @Atomic preferred, as they handle conflicts by restarting the transaction, and take care of proper bookkeeping
is as simple as placing a file fenix-framework.properties on your final application • When it is first needed, the Framework will read the configuration file and start up automatically, taking all of the project’s dependencies as part of the domain
all its classes and relations. How can you get a reference to it? • The framework defines a special DomainRoot class. Invocations of FenixFramework.getDomainRoot() are guaranteed to always return the same instance DomainRoot ApplicationRoot A B
your application? • DomainObject.getExternalId() Returns a (unique) String representation of the object • FenixFramework.getDomainObject(String) Recovers the object by its ID. Results undefined if ID not returned by a call to getExternalId() • FenixFramework.isDomainObjectValid(DomainObject) determines whether the given object is still valid (i.e. is a proper reference, has not been deleted, etc)
can leave the domain in an inconsistent state • Programmer is responsible for specifying constraints on the domain • Two ways to specify Consistency Predicates
and Persistence Support • You should NEVER write code that depends on backends, as it is bound to change drastically, even between minor versions • Two kinds of backends: • In-memory: Useful for testing • Persistence backed: For production usage
• Based on the concept of Versioned Boxes, containing the history of an object’s value • Requires that every stored value is immutable • Changes can only occur within write transactions
of an application • Each Domain Object is stored as a row in the database • Tables organised per type hierarchy • Special FF$ tables are used for bookkeeping and metadata (Specially FF$DOMAIN_CLASS_INFO, which contains class information) • MySQL/MariaDB supported types are used natively when possible (i.e., time, blob)