Skip to content

Schema-Based Repositories

Latest
Compare
Choose a tag to compare
@seandenigris seandenigris released this 16 Jan 18:12
· 55 commits to master since this release

Disclaimer

Not backward compatible with previous versions.

Usage

  1. SpFileDatabase subclasses should respond to class-side #schema with a collection of client classes (i.e. for which we will be handling persistence). For example:
    MyProjectDB class>>#schema
    
    	^ {
    		QuQuote. "a domain class that is part of our project"
    		LivingLibraryDB "the DB from another project (LivingLibraryDB sbclass), which we will take over and persist as part of our model."
    	}.
  2. All client classes (i.e. for which we handle persistence) must respond to #spData with the data to be persisted. SpFileDatabase subclasses get this for free. Here's an example of a domain client class implementation:
    MyDomainClass class>>#spData
    	^ { self uniqueInstance. self tags }
  3. All client classes must respond to #restoreFrom: taking the data they gave me to persist as the argument. Note that this is not an API change for domain classes, but now SpFileDatabase subclasses use the same message (which they get for free), instead of #restoreRepositories:. Here is an example for the domain class in the point above:
    MyDomainClass class>>#restoreFrom: aCollection
    	uniqueInstance := aCollection first.
    	tags := aCollection second.

Motivation

In the past, subclasses would explicitly state how to save and restore each object for which they were responsible. This would result in two methods like the following (taken from a real project):

MySpFileDatabase>>#repositories

	^ { 
			"1." PpLifeHistory mine.
			"2." PpProjectList uniqueInstance.
			"3." ResourcesLiveDB repositories.
			"4." PpTag db.
			"5." PpChecklist pad.
			"6." QuoteMeDB repositories.
			"7." BookmarkMagicDB repositories 
		}.

... and...

MySpFileDatabase>>#restoreRepositories: someRepositories

	PpLifeHistory restoreFrom: someRepositories first.
	PpProjectList restoreFrom: someRepositories second.
	ResourcesLiveDB restoreRepositories: someRepositories third.
	PpTag restoreFrom: someRepositories fourth.
	PpChecklist restoreFrom: someRepositories fifth.
	QuoteMeDB restoreRepositories: someRepositories sixth.
	BookmarkMagicDB restoreRepositories: someRepositories seventh.

There were a few main problems with this:

  1. Not very extensible: every time the persistence of a dependent project would change, the monolithic #repositories method would change. The repo knew too much about the internals of other classes/projects.
  2. Brittle: the order of #restoreRepositories: had to exactly match that of #repositories. Otherwise, the data would be stuffed in the wrong place.
  3. Awkward/Wordy: the data collection had to be manually unpackage (i.e. someRepositories first... someRepositories seventh. Making matters worse, the API was not uniform, with SpFileDatabase subclasses using a different restore message than other classes.

The new API has the following advantages:

  1. Simpler to use and less code to write.
  2. Cleaner Design: Just specify the classes to the DB, not the gorey internal details. Those stay with the client objects where they belong
  3. Robust to schema changes: changes in the number or order of persisted object will not affect older saved data (if it was saved with the new API), because the data carries its own schema around.
  4. Simpler, smaller implementation of this library.