Obscenely simple object persistence
Simple
Low maintenance for real. No instVars. No getters and no setters - just saved models with your data.
Add data to a model →Composable
Do you need to compose models? But of course you do! No mapping required, just add those (sub)models and you're good to go!
Compose a Mapless model →Reactive
Observe model events in one image and react with an observer from another image. All in a scalable way.
React to model events →Start saving objects in seconds
Load in Pharo
Gofer it
smalltalkhubUser: 'Pharo'
project: 'MetaRepoForPharo30';
package: 'ConfigurationOfMapless';
load.
(Smalltalk at: #ConfigurationOfMapless) load
Need to save your models?
Great! this is what happens with Mapless:
- Create a subclass for them
Know in advance what attributes they will needCreate its attributes' instVarsMake accessors for all of themElegantly map them so it all fits in the databasePatiently re-map them every time you need to change its design saying 'hi' to object-database mismatch- Profit
Screencast
How does it look like?
You can store and retrieve your Mapless models on MongoDB in a breeze. Here is a couple of snippets of real code used while developing tasks.
Getting a connection:
Get a connection
"Getting a connection to the 'Reactive' database"
odb := MongoPool instance databaseAt: 'Reactive'.
Creating new instances of your models have nothing special. You treat them like any other object
Create a new model
"Creating a 'task' object"
task := RTask new description: 'Try Mapless'; beIncomplete.
Saving a model
In Mapless you do things inside the #do:[ "your things here" ]
closure which Mapless uses to automatically discern which MongoDB database and collection has to be used. It will automatically know if the save operation has to be an insert or an update. And, as bonus, you also get the collection and the database created lazily.
Saving a model
"Saving the 'task' object"
odb do:[task save].
At flowing we call this a low-friction save.
Getting stored objects
Here you have some basic queries.
Fetching objects
"Getting all models of a given Mapless class"
allTasks := odb do:[RTask findAll].
"Getting a specific model using its id"
theTask := odb do:[RTask findAt: 'c472099f-79b9-8ea2-d61f-e5df34b3ed06'].
Powerful queries
Mapless uses MongoTalk to connect to the database so you have all its power at your fingertips. Need an efficient query? No problem, just make the index in mongoDB, a class method to query on it from your model class and you're ready for your next task.
Querying efficiently
"Query objects using an indexed attribute"
hisTasks := odb do:[RTask findAtUsername: 'awesomeguy'].
Adding stuff to models
Simply do it. Unless overriden, Mapless will catch all messages you send to your models with a DNU and is smart enough to allow you to add a date a string or other objects without declaring instVars or making any accessors.
Adding information in a model
"Add stuff to a model, then save"
odb do:[
task
deadline: Date tomorrow;
notes: 'Wonder how it feels to use this thing, hmm...';
save].
"No, wait! we forgot to add this:"
odb do:[
task
priority: 'High';
save].
"...and this:"
odb do:[
task
tags: #('Cool' 'Stuff');
save].
At flowing we call this low-maintenance storage.
Composition
You add whatever you need to them, even other Mapless models.
When you want to compose your models, the only thing Mapless asks you in return is that your app pays attention to the order in which the models are saved. The way Mapless works is saving the children first. That's not too bad right?
Composing models
"Create and save an alert for the task, then add it and save"
odb do:[ |anAlert|
anAlert := RAlert new
duration: 24 hours;
message: 'Get it done!';
save.
task alert: anAlert.
task save].
Graph navigation
Mapless embraces laziness. When you navigate the object graph, it will return references for every (sub)model known by the root you got. When you send a message to a (sub)model, the #DNU method will return it resolving as if it was always there.
Navigating model graphs
"Lazy access to all model's parts"
odb do:[task alert message].
odb do:[task alert class = MaplessReference].
"=> true"
odb do:[task alert description].
"=> 'Get it done!'"
Mapless powered by Redis
Redis is great for:
- Caching. It will hold the data in RAM so you get great response times.
- Reactivity. There is a set of Mapless that uses Redis PUB/SUB feature to observe/react models among N worker images enabling horizontal scaling.
Here is a workspace for Redis-based Mapless models:
Mapless on Redis
"Get a connection to Redis"
redis := RedisPool instance.
"Subclass MaplessRedis with your models.
As with Mongo, these Mapless have no need
to declare instVars nor setters nor getters"
MaplessRedis subclass: #DummyPerson
instanceVariableNames: ''
classVariableNames: ''
category: 'YourApp'
"Make a new reactive model"
guy := DummyPerson new
firstName: 'John';
lastName: 'Q';
yourself.
"Save it"
redis do:[:r| guy save].
"Find it"
redis do:[:r| DummyPerson findAt: '38skolpqv0y9lazmk772hmsed'].
"Navigate it"
redis do:[:r| (DummyPerson findAt: '38skolpqv0y9lazmk772hmsed') lastName].
"=> 'Q'"
Wait a sec, where's my reactivity there?
Oh snap! you got us there... Reactivity is not ready for prime time yet. But we're getting there. Perhaps you want to join your talent to this effort>?
Why Mapless?
I wanted to persist objects with low friction, low maintenance but high scaling and availability capabilities so Mapless is totally biased towards that. This framework is what I came up with after incorporating my experience with Aggregate in airflowing.
- There is no instVars...
- There is no accessors...
- There is no object-mapping impedance...
- Only persistence.
Frequently Asked Questions
What saving 'Models' means? why not any object? By Models Mapless means that you are not pretending to save transient stuff like contexts or sockets or filehandlers, etc. Any instance of a MongoMapless subclass will save in a breeze. Those instances are going to be serialized and stored as documents of its correspondant MongoDB collection.
Is only for tree-like structures or does actually support an object graph? Given that you follow its rules,like saving children first and thinking your models as noSQL friendly documents, yes, it does support an arbitrary object graph.
Why would I want to use Mapless? Because you might want to profit from some of these benefits:
- JSON friendly models
- Having 1:1 interoperability of your models with Ruby and NodeJS and any JSON friendly object oriented app
- Dealing with Gigas or Teras order of magnitude databases
- High availability
- Easy replication of the whole database across the cloud
- Databases many people is familiar to use and maintain
- Powerful queries and custom indices
- Freedom from a prioristic instVars declarations
- Freedom from mappings maintenance when the design changes
- Trans-image model caching (requires Redis)
- Trans-image model observation/reaction, horizontally scalling the Observer Pattern (requires Redis)
- Friendly to mobile noSQL database use and sync (requires CouchDB)
Get involved!
Want to push this forward? Awesome!
- Clone the repo
- Add your contribution / feature / bugfix
- Send your pull request
Direction?
- We would love to see more and better tests and iterate the design of the reactive features so you can ultimately get an a model in one image being observed in regard to one event by something in another image and that something reacting upon that event. Broadcast and multicast of events would be also a nice feat and not too hard to do. Have design suggestions? Let's have a chat!
- For MongoDB-based Mapless, a nice feature would be to have
UserModel ensureIndex: '{ key1: 1, key2: -1 }'
- We actually are starting to think Amber and localStorage but that's more hush hush at this point. Oh gee! we already blown it!
Want to discuss its design? Lets hangout
License
All Mapless software and the contents of this website are © 2014 by Sebastian Sastre under the terms of MIT License