So far, we've been using the integer serial numbers to refer to audit records when we don't have the record itself. The record can be read and written in a transaction, but outside the transaction we need a non-persistent object to act as a name. The problem with integers is that they aren't typed: whatever the number 42 means in one context is unlikely to be relevant in another. The second problem is that we are deriving the integers from an underlying implementation artefact. The number 42 just identifies an object to most of the code, but it derives from an index into a vector. If we change how the number is generated, then any numbers we already know about would have to change as well.
We need an external naming scheme so we can refer to the audit records in a repository.
- Have a required "name" argument when creating a repository. The name should be immutable and you should be able to access the name without a transaction.
- Define an identifier object.
(defstruct (distributed-identifier (:conc-name did/) (:constructor %make-distributed-identifier (domain repository class numeric-id)) (:copier nil) (:predicate distributed-identifier?)) "DIDs are interned, so you can use EQ to test for equality. However, you should never modify the slots in a DID." (domain "" :read-only t :type string) (repository "" :read-only t :type string) (class nil :read-only t :type (or null keyword)) (numeric-id 0 :read-only t :type non-negative-integer))You can skip the domain element for now, but the other fields are needed.
- Define a printer for a distributed-identifier so they print like this: [test-repository.AUDIT-RECORD.22] the name, class, and numeric id are printed with periods between them.
- Define a parser that can read the fields out of a string.
- Hook up the parser to the reader so that programmers can use the square bracket notation as a literal in a program.
- Intern these objects in a weak hash table. We want to be able to use EQ to test these. So typing (eq [test-repository.AUDIT-RECORD.22] [test-repository.AUDIT-RECORD.22]) will return true. If two distributed-identifiers print the same way, then they refer to the same object and they should be EQ.
- Add a mapping table to the repository for the audit records. You want it so that when an audit record is newly created it will have a new identifier. You'll want to decouple the assignment of the audit-record serial number from the assignment of the numeric ID in the distributed-identifier. This is so we can import audit-records from other repositories.
Consider [test-repository.AUDIT-RECORD.22]. It is the twenty-second audit record created by the test repository, but it is not necessarily at the twenty-second offset in the repository's table of audit-records. It could be, for example, at location 165. The mapping table gives the location. If a new audit-record is created, say at location 166, a new entry in the mapping table will map [test-repository.AUDIT-RECORD.23] to location 166.
The mapping table will be persistent, and thus require a transaction to read.