SlideShare a Scribd company logo
Modeling Tricks My Relational
Database Never Taught Me
David Boike
@DavidBoike
make-awesome.com
About Me
• Principal Consultant, ILM Professional Services
• NServiceBus Champion
• Author of Learning NServiceBus
• Husband, father, geek,
amateur beer brewer
• @DavidBoike
• www.make-awesome.com
SQL Server
SQL Server
SQL Server
• SQL first appeared – 1974
– Hard drive price: $185/MB, $189,000/GB
• Microsoft SQL Server 1.0 – 1989
– Hard drive price: $7.48/MB, $7,659/GB
• Current Version: SQL 2012
– November 2013: 0.004¢/MB, 4.096¢/GB
http://www.jcmit.com/diskprice.htm
Entity Framework
SQL Server
RavenDB
What is RavenDB?
• Fully transactional document database
• Dead simple API
Store(object entity) Delete<T>(T entity)
Load<T>(string id) Query<T>()
• Smart LINQ-powered client library
• Safe by default
• Powerful indexes
• Replication/sharding
• Fraction of SQL Server’s cost
• Fraction of SQL Server’s requirements
RavenDB
Document Modeling
Bad Document Modeling
Take the red pill…
Inheritance
Oh boy, Com Sci 101 again!
Inheritance: Flat
Cliché Polymorphism Examples
• Animals (cat, dog, horse)
• Cars (sedan, truck, minivan, SUV)
– More broadly, machines (bike, motorcycle, car)
• Shapes (circle, rectangle, square)
Inheritance: Table per Class
Ale
BeerId
AleProp1
AleProp2
Beer
BeerId
Type
Name
ABV
IBU
Lager
BeerId
LagerProp1
LagerProp2
Stout
BeerId
StoutProp1
StoutProp2
Inheritance: Table per Top-Level Class
Inheritance: Extended Attributes
AttributeNames:
foo:S:0:11:intVal:S:11:2:
AttributeValues:
Hello World42
Deserialized:
foo=“Hello World”
bar=“42”
Inheritance: XML Attributes
Raven Inheritance
public class BeerListModel
{
public string Id { get; set; }
public List<BeerModel> Beers
{ get; set; }
public BeerListModel()
{
Beers = new List<BeerModel>();
}
}
public abstract class BeerModel
{
public string Name { get; set; }
public decimal ABV { get; set; }
public int IBU { get; set; }
}
public class AleModel : BeerModel
{
public string AleProp1 { get; set; }
public string AleProp2 { get; set; }
}
public class LagerModel : BeerModel
{
public string LagerProp1 { get; set; }
public string LagerProp2 { get; set; }
}
public class StoutModel : BeerModel
{
public string StoutProp1 { get; set; }
public string StoutProp2 { get; set; }
}
Raven Inheritance
Inheritance: Lessons
• Inheritance/Polymorphism in SQL Server sucks
• In RavenDB, it’s no big deal
• Key: With RavenDB we can model things just
like they exist in real life.
Hierarchy
SQL Hierarchy
SELECT *
FROM Category
WHERE SiteId = @SiteId
AND ParentId = @ParentId
SQL Hierarchy
SELECT *
FROM Category
WHERE SiteId = @SiteId
Raven Hierarchy #1
public class CategoryTree
{
// Sites/42/Categories
public string Id { get; set; }
public List<Category> RootCategories { get; set; }
public CategoryTree()
{
RootCategories = new List<Category>();
}
}
Raven Hierarchy #1
public class Category
{
public string CategoryId { get; set; }
public string Name { get; set; }
// Other Stuff
public List<Category> ChildCategories { get; set; }
public Category()
{
ChildCategories = new List<Category>();
}
}
Raven Hierarchy #1
Raven Hierarchy #2
public class Category
{
public string Id { get; set; }
public string Name { get; set; }
// Other Stuff
}
public class CategoryTree
{
public string Id { get; set; }
public List<CategoryRef> RootCategories { get; set; }
}
public class CategoryRef
{
public string CategoryId { get; set; }
public List<CategoryRef> ChildCategories { get; set; }
}
// Don't forget default constructors!
Raven Hierarchy #2
Loading Hierarchy
• Method 1
– One document, just load it
• Method 2
– Multiple Documents
– Normally to load within collections
• .Include("RootCategories,CategoryId")
• Does not work recursively
• Works fine for one level
Loading Hierarchy
var tree = RavenSession
.Load<CategoryTree>("Sites/42/CategoryTree");
// Recursively build list of ids needed
string[] ids = RecurseOnYourOwnTime(tree); // ;-)
var cats = RavenSession
.Load<Category>(ids)
.ToDictionary(doc => doc.Id);
Hierarchy: Lessons
• Model by Units of Change and Transactional
Boundaries
– Guided our choice of hierarchy implementation
– Think about actors in your use cases
• Always initialize collections and child objects
– Nobody likes a NullReferenceException
• We can have global “singleton” documents for
configuration
– Site/Configuration, Site/Categories, etc.
– Great candidates for Aggressive Caching
Duplication
You want me to do what?
Wordpress ERD
Wordpress ERD
Comments
Comment
Meta
Options
Posts
PostMeta
Term
Relationships
Users
UserMeta
Term
Taxonomy
Links
Terms
Copy a Post
• 3 tables involved
– wp_posts
– wp_postmeta (17 types)
– wp_term_relationships
• More if we want to copy comments
– wp_comments
– wp_commentmeta
Copy a Post
• Fairly simple example
– New row in Posts
• Store new PostId
– New rows in PostMeta w/ PostId
– New rows in TermRelationships w/ PostId
• What if we had to copy a table related to
TermRelationships?
• What if an object graph spans dozens of tables?
• What if PostMeta contains hidden ids linking to other
tables?
• P.S. Revisions are stored as additional
records in Posts!
Raven Post Model Example
public class WordPressPost
{
public string Id { get; set; }
public string AuthorId { get; set; }
public DateTimeOffset PostDate { get; set; }
public string Contents { get; set; }
public string Title { get; set; }
public string Excerpt { get; set; }
public PostStatus Status { get; set; }
// etc.
public List<string> Categories { get; set; }
public List<string> Tags { get; set; }
public List<string> Series { get; set; }
public List<string> RelatedPostIds { get; set; }
public List<PostLink> Links { get; set; }
public List<PostMedia> Media { get; set; }
// Comments?...
}
Options for Comments
1. Add to WordPressPost :
public List<PostComment> Comments { get; set; }
2. Document per comment
3. Separate document for comments
• WordPressPosts/1
• WordPressPosts/1/comments
4. Capped documents
• WordPressPosts/1
• WordPressPosts/1/comments/1
• WordPressPosts/1/comments/2
• WordPressPosts/1/comments/3
Capped Documents
Bounded Contexts Unbounded Contexts
Posts/1 Posts/1
Posts/1/Comments
Posts/1/
Comments/3
Posts/1/
Comments/2
Posts/1/
Comments/1
How to duplicate?
// Global.asax / App_Start config
Mapper.Initialize(cfg =>
{
cfg.CreateMap<BlogPost, BlogPost>()
.ForMember(p => p.Id,
opts => opts.Ignore());
});
Can also decorate model’s Id property with
IgnoreMapAttribute but this leaks a dependency to
AutoMapper in your models.
How to duplicate?
BlogPost post =
RavenSession.Load<BlogPost>("BlogPosts/1");
BlogPost dupe = Mapper.Map<BlogPost>(post);
// dupe.Id == null
RavenSession.Store(dupe);
// dupe.Id == "BlogPosts/2"
Why duplicate?
• User Duplicate function (duh)
• Auditing/History
– Posts/1/History/yyyyMMddHHmmss
– Consider Versioning bundle for auditing EVERY
document modification
• Preview
– Posts/1/Preview/ecbe7b49c3de4c8394880bdd81eeae97
• Use Expiration Bundle
• Complex Edit/Publish/Cancel
– Posts/1/Edit/davidboike
• Use Expiration Bundle
Duplication: Lessons
• Thinking about duplication can help to identify
units of change, but can’t get us all the way
• Make use of semantic IDs
• Duplicating data enables many advanced
scenarios
• Think about possible document growth
• Break down large documents within one unit
of change into slimmer documents
NServiceBus Saga Storage
Hey remember that book I
mentioned?
NServiceBus/Sagas 101
• NServiceBus
– Framework for creating distributed systems
– Transactional message handlers w/ auto-retry
– Publish/Subscribe
• NServiceBus Sagas
– Correlated message handlers with shared state
– Transactional state stored between messages
– Similar in concept to ASP.NET Session State
Saga Storage Interface
public interface ISagaPersister
{
T Get<T>(Guid sagaId);
T Get<T>(string property, object value);
void Save(IContainSagaData saga);
void Update(IContainSagaData saga);
void Complete(IContainSagaData saga);
}
public interface IContainSagaData
{
Guid Id { get; set; }
string Originator { get; set; }
string OriginalMessageId { get; set; }
}
Saga Storage History
• NServiceBus 2.0
– SQL Storage via Fluent NHibernate
• It sucked
– Built my own based on XmlSerializer
• That also sucked, just not as much
• NServiceBus 3.0
– RavenDB Saga Persister became default
– Villagers rejoiced!
Fluent NHibernate
public class MySagaData : IContainSagaData
{
public virtual int MessagesReceived { get; set; }
// Don't touch! NServiceBus uses these internally!
public virtual Guid Id { get; set; }
public virtual string Originator { get; set; }
public virtual string OriginalMessageId { get; set; }
}
MySagaData
Id
Originator
OriginalMessageId
MessagesReceived
Fluent NHibernate
public class MySagaData : IContainSagaData
{
public virtual int MessagesReceived { get; set; }
// Don't touch! NServiceBus uses these internally!
public virtual Guid Id { get; set; }
public virtual string Originator { get; set; }
public virtual string OriginalMessageId { get; set; }
}
MySagaData
Id
Originator
OriginalMessageId
MessagesReceived
Fluent NHibernate
public class MySagaData : BaseSagaData
{
public virtual int MessagesReceived { get; set; }
}
public abstract class BaseSagaData : IContainSagaData
{
public virtual Guid Id { get; set; }
public virtual string Originator { get; set; }
public virtual string OriginalMessageId { get; set; }
}
MySagaData
Id
MessagesReceived
BaseSagaData
Id
Originator
OriginalMessageId
Fluent NHibernate
public class MySagaData : IContainSagaData
{
public virtual List<string> MessageTypesReceived { get; set; }
// Don't touch!
public virtual Guid Id { get; set; }
public virtual string Originator { get; set; }
public virtual string OriginalMessageId { get; set; }
}
Fluent NHibernate
public class MySagaData : IContainSagaData
{
public virtual List<string> MessageTypesReceived { get; set; }
// Don't touch!
public virtual Guid Id { get; set; }
public virtual string Originator { get; set; }
public virtual string OriginalMessageId { get; set; }
}
Fluent NHibernate
public class MySagaData : IContainSagaData
{
public virtual List<ReceivedMessage> MessageTypesReceived { get; set; }
// Don't touch!
public virtual Guid Id { get; set; }
public virtual string Originator { get; set; }
public virtual string OriginalMessageId { get; set; }
}
public class ReceivedMessage
{
public virtual Guid Id { get; set; }
public virtual string MessageTypeReceived { get; set; }
}
MySagaData
Id
Originator
OriginalMessageId
ReceivedMessage
Id
MySagaDataId
MessageTypeReceived
Raven Saga Persistence
public class MySagaData : ContainSagaData
{
public List<string> MessageTypesReceived { get; set; }
}
-OR-
public class MySagaData : ContainSagaData
{
public HashSet<Type> MessageTypesReceived { get; set; }
}
Back to ISagaPersister
public interface ISagaPersister
{
T Get<T>(Guid sagaId);
T Get<T>(string property, object value);
void Save(IContainSagaData saga);
void Update(IContainSagaData saga);
void Complete(IContainSagaData saga);
}
Need to be able to load a saga by any identifiable
property that matches on the incoming message
and saga data.
More Real-Life Saga
Shipping Saga:
• Waits for OrderAccepted + OrderBilled events
• Publishes OrderShipped event
public class ShippingData : ContainSagaData
{
[Unique]
public long OrderId { get; set; }
public List<string> MessageTypesReceived { get; set; }
}
UniqueAttribute
• NHibernate persister would create a unique index on the column
• RavenDB indexes are asynchronous and NOT consistent
– Consistency only guaranteed on Store/Load by ID
Pointer Documents
// PubSub.Shipping.ShippingData/OrderId/{GUID-1}
{
"SagaId": "{GUID-2}",
"UniqueValue": 12345,
"SagaDocId": “ShippingData/{GUID-2}"
}
// ShippingData/{GUID-2}
{
"OrderId": 12345,
"Originator": "PubSub.Sales@ILM-LION",
"OriginalMessageId": "{GUID-3}"
}
OpenID/OAuth
• Login with multiple 3rd-party identities also requires
pointer document modeling
• OpenID identity URLs make for horrible document IDs
– Use the SHA1 hash of the login provider and login
identifier in the document ID instead.
• Easy way with MVC5
– RavenDB.AspNet.Identity NuGet package
– https://github.com/ILMServices/RavenDB.AspNet.Identity
– At least look at the source and make fun of the author
(That’s me)
RavenDB.AspNet.Identity
{ // ApplicationUsers/DavidBoike (Some boring properties removed)
"UserName": "DavidBoike",
"Logins": [
{
"LoginProvider": "Google",
"ProviderKey": "https://www.google.com/accounts/o8/id?id=AItOawnt..."
}
]
}
{ // IdentityUserLogins/95c8f50aa725d62af9c7e397ca3a89889dd76514
"UserId": "ApplicationUsers/DavidBoike",
"Provider": "Google",
"ProviderKey": "https://www.google.com/accounts/o8/id?id=AItOawnt..."
}
NServiceBus: Lessons
• Document databases are ideal for storing
random objects with little friction
• Unique constraints must be treated specially
to ensure consistency
– Custom pointer documents
– Unique Constraints bundle for more generic
functionality (though I prefer not to overuse it)
• Use RavenDB.AspNet.Identity for OpenID
– Better yet send me a pull request!
Final Thoughts
Document modeling is not the same as SQL.
If you try to make it so, you will fail.
So don’t be that guy.
Final Thoughts
Above all else, think about how and why data
changes.
– Units of Change
– Transactional Boundaries
– Everything else is window dressing
Final Thoughts
Like relational modeling, document modeling is
a skill that is acquired through experience
– Model things like you would in the real world
– Don’t be afraid of trying and failing
– Create a /FakeData/Create action in your site to
bootstrap your database to make this easy
– Especially in the early stages of an application,
experiment often and regenerate when needed
Questions?
@DavidBoike
make-awesome.com

More Related Content

What's hot

Day 7 - Make it Fast
Day 7 - Make it FastDay 7 - Make it Fast
Day 7 - Make it Fast
Barry Jones
 
Integration patterns in AEM 6
Integration patterns in AEM 6Integration patterns in AEM 6
Integration patterns in AEM 6
Yuval Ararat
 
Ruby - a tester's best friend
Ruby - a tester's best friendRuby - a tester's best friend
Ruby - a tester's best friendPeter Lind
 
Concurrency in Scala - the Akka way
Concurrency in Scala - the Akka wayConcurrency in Scala - the Akka way
Concurrency in Scala - the Akka wayYardena Meymann
 
DSpace 4.2 Basics & Configuration
DSpace 4.2 Basics & ConfigurationDSpace 4.2 Basics & Configuration
DSpace 4.2 Basics & ConfigurationDuraSpace
 
Above the clouds: introducing Akka
Above the clouds: introducing AkkaAbove the clouds: introducing Akka
Above the clouds: introducing Akka
nartamonov
 
Scala Matsuri 2016: Japanese Text Mining with Scala and Spark
Scala Matsuri 2016: Japanese Text Mining with Scala and SparkScala Matsuri 2016: Japanese Text Mining with Scala and Spark
Scala Matsuri 2016: Japanese Text Mining with Scala and Spark
Eduardo Gonzalez
 
Hands on with Ruby & MongoDB
Hands on with Ruby & MongoDBHands on with Ruby & MongoDB
Hands on with Ruby & MongoDB
Wynn Netherland
 
Leveraging Open Source for Database Development: Database Version Control wit...
Leveraging Open Source for Database Development: Database Version Control wit...Leveraging Open Source for Database Development: Database Version Control wit...
Leveraging Open Source for Database Development: Database Version Control wit...
All Things Open
 
DSpace 4.2 Transmission: Import/Export
DSpace 4.2 Transmission: Import/ExportDSpace 4.2 Transmission: Import/Export
DSpace 4.2 Transmission: Import/ExportDuraSpace
 
DjangoCon 2010 Scaling Disqus
DjangoCon 2010 Scaling DisqusDjangoCon 2010 Scaling Disqus
DjangoCon 2010 Scaling Disqus
zeeg
 
High-Performance Hibernate Devoxx France 2016
High-Performance Hibernate Devoxx France 2016High-Performance Hibernate Devoxx France 2016
High-Performance Hibernate Devoxx France 2016
Vlad Mihalcea
 
Synthetic models
Synthetic modelsSynthetic models
Synthetic models
Zoran Nikolovski
 
Day 2 - Intro to Rails
Day 2 - Intro to RailsDay 2 - Intro to Rails
Day 2 - Intro to Rails
Barry Jones
 
Day 4 - Models
Day 4 - ModelsDay 4 - Models
Day 4 - Models
Barry Jones
 
Building Reactive Systems with Akka (in Java 8 or Scala)
Building Reactive Systems with Akka (in Java 8 or Scala)Building Reactive Systems with Akka (in Java 8 or Scala)
Building Reactive Systems with Akka (in Java 8 or Scala)
Jonas Bonér
 
The Return of the Living Datalog
The Return of the Living DatalogThe Return of the Living Datalog
The Return of the Living Datalog
Mike Fogus
 
Lessons from a Dying CMS
Lessons from a Dying CMSLessons from a Dying CMS
Lessons from a Dying CMS
Sandy Smith
 
What You Missed in Computer Science
What You Missed in Computer ScienceWhat You Missed in Computer Science
What You Missed in Computer Science
Taylor Lovett
 
ClojureScript Anatomy
ClojureScript AnatomyClojureScript Anatomy
ClojureScript Anatomy
Mike Fogus
 

What's hot (20)

Day 7 - Make it Fast
Day 7 - Make it FastDay 7 - Make it Fast
Day 7 - Make it Fast
 
Integration patterns in AEM 6
Integration patterns in AEM 6Integration patterns in AEM 6
Integration patterns in AEM 6
 
Ruby - a tester's best friend
Ruby - a tester's best friendRuby - a tester's best friend
Ruby - a tester's best friend
 
Concurrency in Scala - the Akka way
Concurrency in Scala - the Akka wayConcurrency in Scala - the Akka way
Concurrency in Scala - the Akka way
 
DSpace 4.2 Basics & Configuration
DSpace 4.2 Basics & ConfigurationDSpace 4.2 Basics & Configuration
DSpace 4.2 Basics & Configuration
 
Above the clouds: introducing Akka
Above the clouds: introducing AkkaAbove the clouds: introducing Akka
Above the clouds: introducing Akka
 
Scala Matsuri 2016: Japanese Text Mining with Scala and Spark
Scala Matsuri 2016: Japanese Text Mining with Scala and SparkScala Matsuri 2016: Japanese Text Mining with Scala and Spark
Scala Matsuri 2016: Japanese Text Mining with Scala and Spark
 
Hands on with Ruby & MongoDB
Hands on with Ruby & MongoDBHands on with Ruby & MongoDB
Hands on with Ruby & MongoDB
 
Leveraging Open Source for Database Development: Database Version Control wit...
Leveraging Open Source for Database Development: Database Version Control wit...Leveraging Open Source for Database Development: Database Version Control wit...
Leveraging Open Source for Database Development: Database Version Control wit...
 
DSpace 4.2 Transmission: Import/Export
DSpace 4.2 Transmission: Import/ExportDSpace 4.2 Transmission: Import/Export
DSpace 4.2 Transmission: Import/Export
 
DjangoCon 2010 Scaling Disqus
DjangoCon 2010 Scaling DisqusDjangoCon 2010 Scaling Disqus
DjangoCon 2010 Scaling Disqus
 
High-Performance Hibernate Devoxx France 2016
High-Performance Hibernate Devoxx France 2016High-Performance Hibernate Devoxx France 2016
High-Performance Hibernate Devoxx France 2016
 
Synthetic models
Synthetic modelsSynthetic models
Synthetic models
 
Day 2 - Intro to Rails
Day 2 - Intro to RailsDay 2 - Intro to Rails
Day 2 - Intro to Rails
 
Day 4 - Models
Day 4 - ModelsDay 4 - Models
Day 4 - Models
 
Building Reactive Systems with Akka (in Java 8 or Scala)
Building Reactive Systems with Akka (in Java 8 or Scala)Building Reactive Systems with Akka (in Java 8 or Scala)
Building Reactive Systems with Akka (in Java 8 or Scala)
 
The Return of the Living Datalog
The Return of the Living DatalogThe Return of the Living Datalog
The Return of the Living Datalog
 
Lessons from a Dying CMS
Lessons from a Dying CMSLessons from a Dying CMS
Lessons from a Dying CMS
 
What You Missed in Computer Science
What You Missed in Computer ScienceWhat You Missed in Computer Science
What You Missed in Computer Science
 
ClojureScript Anatomy
ClojureScript AnatomyClojureScript Anatomy
ClojureScript Anatomy
 

Similar to Modeling Tricks My Relational Database Never Taught Me

Supercharging WordPress Development in 2018
Supercharging WordPress Development in 2018Supercharging WordPress Development in 2018
Supercharging WordPress Development in 2018
Adam Tomat
 
Using Document Databases with TYPO3 Flow
Using Document Databases with TYPO3 FlowUsing Document Databases with TYPO3 Flow
Using Document Databases with TYPO3 Flow
Karsten Dambekalns
 
Solid And Sustainable Development in Scala
Solid And Sustainable Development in ScalaSolid And Sustainable Development in Scala
Solid And Sustainable Development in Scala
Kazuhiro Sera
 
A Practical Multi-Tenant Cluster
A Practical Multi-Tenant ClusterA Practical Multi-Tenant Cluster
A Practical Multi-Tenant Cluster
Command Prompt., Inc
 
Solid and Sustainable Development in Scala
Solid and Sustainable Development in ScalaSolid and Sustainable Development in Scala
Solid and Sustainable Development in Scala
scalaconfjp
 
MariaDB Server Compatibility with MySQL
MariaDB Server Compatibility with MySQLMariaDB Server Compatibility with MySQL
MariaDB Server Compatibility with MySQL
Colin Charles
 
Deep Dive into Cassandra
Deep Dive into CassandraDeep Dive into Cassandra
Deep Dive into Cassandra
Brent Theisen
 
CouchDB for Web Applications - Erlang Factory London 2009
CouchDB for Web Applications - Erlang Factory London 2009CouchDB for Web Applications - Erlang Factory London 2009
CouchDB for Web Applications - Erlang Factory London 2009
Jason Davies
 
dotNet Miami - June 21, 2012: Richie Rump: Entity Framework: Code First and M...
dotNet Miami - June 21, 2012: Richie Rump: Entity Framework: Code First and M...dotNet Miami - June 21, 2012: Richie Rump: Entity Framework: Code First and M...
dotNet Miami - June 21, 2012: Richie Rump: Entity Framework: Code First and M...
dotNet Miami
 
Entity Framework: Code First and Magic Unicorns
Entity Framework: Code First and Magic UnicornsEntity Framework: Code First and Magic Unicorns
Entity Framework: Code First and Magic UnicornsRichie Rump
 
WordPress Café: Using WordPress as a Framework
WordPress Café: Using WordPress as a FrameworkWordPress Café: Using WordPress as a Framework
WordPress Café: Using WordPress as a Framework
Exove
 
Spring data presentation
Spring data presentationSpring data presentation
Spring data presentation
Oleksii Usyk
 
4η διάλεξη Τεχνολογίες Παγκόσμιου Ιστού
4η διάλεξη Τεχνολογίες Παγκόσμιου Ιστού4η διάλεξη Τεχνολογίες Παγκόσμιου Ιστού
4η διάλεξη Τεχνολογίες Παγκόσμιου Ιστού
Manolis Vavalis
 
One to Many: The Story of Sharding at Box
One to Many: The Story of Sharding at BoxOne to Many: The Story of Sharding at Box
One to Many: The Story of Sharding at Box
Florian Jourda
 
Learning to run
Learning to runLearning to run
Learning to run
dominion
 
Meetup developing building and_deploying databases with SSDT
Meetup developing building and_deploying databases with SSDTMeetup developing building and_deploying databases with SSDT
Meetup developing building and_deploying databases with SSDT
Solidify
 
Web Development with Python and Django
Web Development with Python and DjangoWeb Development with Python and Django
Web Development with Python and Django
Michael Pirnat
 
REST easy with API Platform
REST easy with API PlatformREST easy with API Platform
REST easy with API Platform
Antonio Peric-Mazar
 
Functional Hostnames and Why they are Bad
Functional Hostnames and Why they are BadFunctional Hostnames and Why they are Bad
Functional Hostnames and Why they are Bad
Puppet
 
Back to Basics: Build Something Big With MongoDB
Back to Basics: Build Something Big With MongoDB Back to Basics: Build Something Big With MongoDB
Back to Basics: Build Something Big With MongoDB MongoDB
 

Similar to Modeling Tricks My Relational Database Never Taught Me (20)

Supercharging WordPress Development in 2018
Supercharging WordPress Development in 2018Supercharging WordPress Development in 2018
Supercharging WordPress Development in 2018
 
Using Document Databases with TYPO3 Flow
Using Document Databases with TYPO3 FlowUsing Document Databases with TYPO3 Flow
Using Document Databases with TYPO3 Flow
 
Solid And Sustainable Development in Scala
Solid And Sustainable Development in ScalaSolid And Sustainable Development in Scala
Solid And Sustainable Development in Scala
 
A Practical Multi-Tenant Cluster
A Practical Multi-Tenant ClusterA Practical Multi-Tenant Cluster
A Practical Multi-Tenant Cluster
 
Solid and Sustainable Development in Scala
Solid and Sustainable Development in ScalaSolid and Sustainable Development in Scala
Solid and Sustainable Development in Scala
 
MariaDB Server Compatibility with MySQL
MariaDB Server Compatibility with MySQLMariaDB Server Compatibility with MySQL
MariaDB Server Compatibility with MySQL
 
Deep Dive into Cassandra
Deep Dive into CassandraDeep Dive into Cassandra
Deep Dive into Cassandra
 
CouchDB for Web Applications - Erlang Factory London 2009
CouchDB for Web Applications - Erlang Factory London 2009CouchDB for Web Applications - Erlang Factory London 2009
CouchDB for Web Applications - Erlang Factory London 2009
 
dotNet Miami - June 21, 2012: Richie Rump: Entity Framework: Code First and M...
dotNet Miami - June 21, 2012: Richie Rump: Entity Framework: Code First and M...dotNet Miami - June 21, 2012: Richie Rump: Entity Framework: Code First and M...
dotNet Miami - June 21, 2012: Richie Rump: Entity Framework: Code First and M...
 
Entity Framework: Code First and Magic Unicorns
Entity Framework: Code First and Magic UnicornsEntity Framework: Code First and Magic Unicorns
Entity Framework: Code First and Magic Unicorns
 
WordPress Café: Using WordPress as a Framework
WordPress Café: Using WordPress as a FrameworkWordPress Café: Using WordPress as a Framework
WordPress Café: Using WordPress as a Framework
 
Spring data presentation
Spring data presentationSpring data presentation
Spring data presentation
 
4η διάλεξη Τεχνολογίες Παγκόσμιου Ιστού
4η διάλεξη Τεχνολογίες Παγκόσμιου Ιστού4η διάλεξη Τεχνολογίες Παγκόσμιου Ιστού
4η διάλεξη Τεχνολογίες Παγκόσμιου Ιστού
 
One to Many: The Story of Sharding at Box
One to Many: The Story of Sharding at BoxOne to Many: The Story of Sharding at Box
One to Many: The Story of Sharding at Box
 
Learning to run
Learning to runLearning to run
Learning to run
 
Meetup developing building and_deploying databases with SSDT
Meetup developing building and_deploying databases with SSDTMeetup developing building and_deploying databases with SSDT
Meetup developing building and_deploying databases with SSDT
 
Web Development with Python and Django
Web Development with Python and DjangoWeb Development with Python and Django
Web Development with Python and Django
 
REST easy with API Platform
REST easy with API PlatformREST easy with API Platform
REST easy with API Platform
 
Functional Hostnames and Why they are Bad
Functional Hostnames and Why they are BadFunctional Hostnames and Why they are Bad
Functional Hostnames and Why they are Bad
 
Back to Basics: Build Something Big With MongoDB
Back to Basics: Build Something Big With MongoDB Back to Basics: Build Something Big With MongoDB
Back to Basics: Build Something Big With MongoDB
 

Recently uploaded

Announcement of 18th IEEE International Conference on Software Testing, Verif...
Announcement of 18th IEEE International Conference on Software Testing, Verif...Announcement of 18th IEEE International Conference on Software Testing, Verif...
Announcement of 18th IEEE International Conference on Software Testing, Verif...
Sebastiano Panichella
 
Bitcoin Lightning wallet and tic-tac-toe game XOXO
Bitcoin Lightning wallet and tic-tac-toe game XOXOBitcoin Lightning wallet and tic-tac-toe game XOXO
Bitcoin Lightning wallet and tic-tac-toe game XOXO
Matjaž Lipuš
 
Sharpen existing tools or get a new toolbox? Contemporary cluster initiatives...
Sharpen existing tools or get a new toolbox? Contemporary cluster initiatives...Sharpen existing tools or get a new toolbox? Contemporary cluster initiatives...
Sharpen existing tools or get a new toolbox? Contemporary cluster initiatives...
Orkestra
 
somanykidsbutsofewfathers-140705000023-phpapp02.pptx
somanykidsbutsofewfathers-140705000023-phpapp02.pptxsomanykidsbutsofewfathers-140705000023-phpapp02.pptx
somanykidsbutsofewfathers-140705000023-phpapp02.pptx
Howard Spence
 
Bonzo subscription_hjjjjjjjj5hhhhhhh_2024.pdf
Bonzo subscription_hjjjjjjjj5hhhhhhh_2024.pdfBonzo subscription_hjjjjjjjj5hhhhhhh_2024.pdf
Bonzo subscription_hjjjjjjjj5hhhhhhh_2024.pdf
khadija278284
 
María Carolina Martínez - eCommerce Day Colombia 2024
María Carolina Martínez - eCommerce Day Colombia 2024María Carolina Martínez - eCommerce Day Colombia 2024
María Carolina Martínez - eCommerce Day Colombia 2024
eCommerce Institute
 
0x01 - Newton's Third Law: Static vs. Dynamic Abusers
0x01 - Newton's Third Law:  Static vs. Dynamic Abusers0x01 - Newton's Third Law:  Static vs. Dynamic Abusers
0x01 - Newton's Third Law: Static vs. Dynamic Abusers
OWASP Beja
 
Acorn Recovery: Restore IT infra within minutes
Acorn Recovery: Restore IT infra within minutesAcorn Recovery: Restore IT infra within minutes
Acorn Recovery: Restore IT infra within minutes
IP ServerOne
 
International Workshop on Artificial Intelligence in Software Testing
International Workshop on Artificial Intelligence in Software TestingInternational Workshop on Artificial Intelligence in Software Testing
International Workshop on Artificial Intelligence in Software Testing
Sebastiano Panichella
 
Getting started with Amazon Bedrock Studio and Control Tower
Getting started with Amazon Bedrock Studio and Control TowerGetting started with Amazon Bedrock Studio and Control Tower
Getting started with Amazon Bedrock Studio and Control Tower
Vladimir Samoylov
 
Media as a Mind Controlling Strategy In Old and Modern Era
Media as a Mind Controlling Strategy In Old and Modern EraMedia as a Mind Controlling Strategy In Old and Modern Era
Media as a Mind Controlling Strategy In Old and Modern Era
faizulhassanfaiz1670
 
Obesity causes and management and associated medical conditions
Obesity causes and management and associated medical conditionsObesity causes and management and associated medical conditions
Obesity causes and management and associated medical conditions
Faculty of Medicine And Health Sciences
 
Doctoral Symposium at the 17th IEEE International Conference on Software Test...
Doctoral Symposium at the 17th IEEE International Conference on Software Test...Doctoral Symposium at the 17th IEEE International Conference on Software Test...
Doctoral Symposium at the 17th IEEE International Conference on Software Test...
Sebastiano Panichella
 
Eureka, I found it! - Special Libraries Association 2021 Presentation
Eureka, I found it! - Special Libraries Association 2021 PresentationEureka, I found it! - Special Libraries Association 2021 Presentation
Eureka, I found it! - Special Libraries Association 2021 Presentation
Access Innovations, Inc.
 
Supercharge your AI - SSP Industry Breakout Session 2024-v2_1.pdf
Supercharge your AI - SSP Industry Breakout Session 2024-v2_1.pdfSupercharge your AI - SSP Industry Breakout Session 2024-v2_1.pdf
Supercharge your AI - SSP Industry Breakout Session 2024-v2_1.pdf
Access Innovations, Inc.
 
Competition and Regulation in Professional Services – KLEINER – June 2024 OEC...
Competition and Regulation in Professional Services – KLEINER – June 2024 OEC...Competition and Regulation in Professional Services – KLEINER – June 2024 OEC...
Competition and Regulation in Professional Services – KLEINER – June 2024 OEC...
OECD Directorate for Financial and Enterprise Affairs
 

Recently uploaded (16)

Announcement of 18th IEEE International Conference on Software Testing, Verif...
Announcement of 18th IEEE International Conference on Software Testing, Verif...Announcement of 18th IEEE International Conference on Software Testing, Verif...
Announcement of 18th IEEE International Conference on Software Testing, Verif...
 
Bitcoin Lightning wallet and tic-tac-toe game XOXO
Bitcoin Lightning wallet and tic-tac-toe game XOXOBitcoin Lightning wallet and tic-tac-toe game XOXO
Bitcoin Lightning wallet and tic-tac-toe game XOXO
 
Sharpen existing tools or get a new toolbox? Contemporary cluster initiatives...
Sharpen existing tools or get a new toolbox? Contemporary cluster initiatives...Sharpen existing tools or get a new toolbox? Contemporary cluster initiatives...
Sharpen existing tools or get a new toolbox? Contemporary cluster initiatives...
 
somanykidsbutsofewfathers-140705000023-phpapp02.pptx
somanykidsbutsofewfathers-140705000023-phpapp02.pptxsomanykidsbutsofewfathers-140705000023-phpapp02.pptx
somanykidsbutsofewfathers-140705000023-phpapp02.pptx
 
Bonzo subscription_hjjjjjjjj5hhhhhhh_2024.pdf
Bonzo subscription_hjjjjjjjj5hhhhhhh_2024.pdfBonzo subscription_hjjjjjjjj5hhhhhhh_2024.pdf
Bonzo subscription_hjjjjjjjj5hhhhhhh_2024.pdf
 
María Carolina Martínez - eCommerce Day Colombia 2024
María Carolina Martínez - eCommerce Day Colombia 2024María Carolina Martínez - eCommerce Day Colombia 2024
María Carolina Martínez - eCommerce Day Colombia 2024
 
0x01 - Newton's Third Law: Static vs. Dynamic Abusers
0x01 - Newton's Third Law:  Static vs. Dynamic Abusers0x01 - Newton's Third Law:  Static vs. Dynamic Abusers
0x01 - Newton's Third Law: Static vs. Dynamic Abusers
 
Acorn Recovery: Restore IT infra within minutes
Acorn Recovery: Restore IT infra within minutesAcorn Recovery: Restore IT infra within minutes
Acorn Recovery: Restore IT infra within minutes
 
International Workshop on Artificial Intelligence in Software Testing
International Workshop on Artificial Intelligence in Software TestingInternational Workshop on Artificial Intelligence in Software Testing
International Workshop on Artificial Intelligence in Software Testing
 
Getting started with Amazon Bedrock Studio and Control Tower
Getting started with Amazon Bedrock Studio and Control TowerGetting started with Amazon Bedrock Studio and Control Tower
Getting started with Amazon Bedrock Studio and Control Tower
 
Media as a Mind Controlling Strategy In Old and Modern Era
Media as a Mind Controlling Strategy In Old and Modern EraMedia as a Mind Controlling Strategy In Old and Modern Era
Media as a Mind Controlling Strategy In Old and Modern Era
 
Obesity causes and management and associated medical conditions
Obesity causes and management and associated medical conditionsObesity causes and management and associated medical conditions
Obesity causes and management and associated medical conditions
 
Doctoral Symposium at the 17th IEEE International Conference on Software Test...
Doctoral Symposium at the 17th IEEE International Conference on Software Test...Doctoral Symposium at the 17th IEEE International Conference on Software Test...
Doctoral Symposium at the 17th IEEE International Conference on Software Test...
 
Eureka, I found it! - Special Libraries Association 2021 Presentation
Eureka, I found it! - Special Libraries Association 2021 PresentationEureka, I found it! - Special Libraries Association 2021 Presentation
Eureka, I found it! - Special Libraries Association 2021 Presentation
 
Supercharge your AI - SSP Industry Breakout Session 2024-v2_1.pdf
Supercharge your AI - SSP Industry Breakout Session 2024-v2_1.pdfSupercharge your AI - SSP Industry Breakout Session 2024-v2_1.pdf
Supercharge your AI - SSP Industry Breakout Session 2024-v2_1.pdf
 
Competition and Regulation in Professional Services – KLEINER – June 2024 OEC...
Competition and Regulation in Professional Services – KLEINER – June 2024 OEC...Competition and Regulation in Professional Services – KLEINER – June 2024 OEC...
Competition and Regulation in Professional Services – KLEINER – June 2024 OEC...
 

Modeling Tricks My Relational Database Never Taught Me

  • 1. Modeling Tricks My Relational Database Never Taught Me David Boike @DavidBoike make-awesome.com
  • 2. About Me • Principal Consultant, ILM Professional Services • NServiceBus Champion • Author of Learning NServiceBus • Husband, father, geek, amateur beer brewer • @DavidBoike • www.make-awesome.com
  • 5. SQL Server • SQL first appeared – 1974 – Hard drive price: $185/MB, $189,000/GB • Microsoft SQL Server 1.0 – 1989 – Hard drive price: $7.48/MB, $7,659/GB • Current Version: SQL 2012 – November 2013: 0.004¢/MB, 4.096¢/GB http://www.jcmit.com/diskprice.htm
  • 6.
  • 8.
  • 11. What is RavenDB? • Fully transactional document database • Dead simple API Store(object entity) Delete<T>(T entity) Load<T>(string id) Query<T>() • Smart LINQ-powered client library • Safe by default • Powerful indexes • Replication/sharding • Fraction of SQL Server’s cost • Fraction of SQL Server’s requirements
  • 15. Take the red pill…
  • 16. Inheritance Oh boy, Com Sci 101 again!
  • 18. Cliché Polymorphism Examples • Animals (cat, dog, horse) • Cars (sedan, truck, minivan, SUV) – More broadly, machines (bike, motorcycle, car) • Shapes (circle, rectangle, square)
  • 19. Inheritance: Table per Class Ale BeerId AleProp1 AleProp2 Beer BeerId Type Name ABV IBU Lager BeerId LagerProp1 LagerProp2 Stout BeerId StoutProp1 StoutProp2
  • 20. Inheritance: Table per Top-Level Class
  • 23. Raven Inheritance public class BeerListModel { public string Id { get; set; } public List<BeerModel> Beers { get; set; } public BeerListModel() { Beers = new List<BeerModel>(); } } public abstract class BeerModel { public string Name { get; set; } public decimal ABV { get; set; } public int IBU { get; set; } } public class AleModel : BeerModel { public string AleProp1 { get; set; } public string AleProp2 { get; set; } } public class LagerModel : BeerModel { public string LagerProp1 { get; set; } public string LagerProp2 { get; set; } } public class StoutModel : BeerModel { public string StoutProp1 { get; set; } public string StoutProp2 { get; set; } }
  • 25. Inheritance: Lessons • Inheritance/Polymorphism in SQL Server sucks • In RavenDB, it’s no big deal • Key: With RavenDB we can model things just like they exist in real life.
  • 27. SQL Hierarchy SELECT * FROM Category WHERE SiteId = @SiteId AND ParentId = @ParentId
  • 28. SQL Hierarchy SELECT * FROM Category WHERE SiteId = @SiteId
  • 29. Raven Hierarchy #1 public class CategoryTree { // Sites/42/Categories public string Id { get; set; } public List<Category> RootCategories { get; set; } public CategoryTree() { RootCategories = new List<Category>(); } }
  • 30. Raven Hierarchy #1 public class Category { public string CategoryId { get; set; } public string Name { get; set; } // Other Stuff public List<Category> ChildCategories { get; set; } public Category() { ChildCategories = new List<Category>(); } }
  • 32. Raven Hierarchy #2 public class Category { public string Id { get; set; } public string Name { get; set; } // Other Stuff } public class CategoryTree { public string Id { get; set; } public List<CategoryRef> RootCategories { get; set; } } public class CategoryRef { public string CategoryId { get; set; } public List<CategoryRef> ChildCategories { get; set; } } // Don't forget default constructors!
  • 34. Loading Hierarchy • Method 1 – One document, just load it • Method 2 – Multiple Documents – Normally to load within collections • .Include("RootCategories,CategoryId") • Does not work recursively • Works fine for one level
  • 35. Loading Hierarchy var tree = RavenSession .Load<CategoryTree>("Sites/42/CategoryTree"); // Recursively build list of ids needed string[] ids = RecurseOnYourOwnTime(tree); // ;-) var cats = RavenSession .Load<Category>(ids) .ToDictionary(doc => doc.Id);
  • 36. Hierarchy: Lessons • Model by Units of Change and Transactional Boundaries – Guided our choice of hierarchy implementation – Think about actors in your use cases • Always initialize collections and child objects – Nobody likes a NullReferenceException • We can have global “singleton” documents for configuration – Site/Configuration, Site/Categories, etc. – Great candidates for Aggressive Caching
  • 37. Duplication You want me to do what?
  • 40. Copy a Post • 3 tables involved – wp_posts – wp_postmeta (17 types) – wp_term_relationships • More if we want to copy comments – wp_comments – wp_commentmeta
  • 41. Copy a Post • Fairly simple example – New row in Posts • Store new PostId – New rows in PostMeta w/ PostId – New rows in TermRelationships w/ PostId • What if we had to copy a table related to TermRelationships? • What if an object graph spans dozens of tables? • What if PostMeta contains hidden ids linking to other tables? • P.S. Revisions are stored as additional records in Posts!
  • 42. Raven Post Model Example public class WordPressPost { public string Id { get; set; } public string AuthorId { get; set; } public DateTimeOffset PostDate { get; set; } public string Contents { get; set; } public string Title { get; set; } public string Excerpt { get; set; } public PostStatus Status { get; set; } // etc. public List<string> Categories { get; set; } public List<string> Tags { get; set; } public List<string> Series { get; set; } public List<string> RelatedPostIds { get; set; } public List<PostLink> Links { get; set; } public List<PostMedia> Media { get; set; } // Comments?... }
  • 43. Options for Comments 1. Add to WordPressPost : public List<PostComment> Comments { get; set; } 2. Document per comment 3. Separate document for comments • WordPressPosts/1 • WordPressPosts/1/comments 4. Capped documents • WordPressPosts/1 • WordPressPosts/1/comments/1 • WordPressPosts/1/comments/2 • WordPressPosts/1/comments/3
  • 44. Capped Documents Bounded Contexts Unbounded Contexts Posts/1 Posts/1 Posts/1/Comments Posts/1/ Comments/3 Posts/1/ Comments/2 Posts/1/ Comments/1
  • 45. How to duplicate? // Global.asax / App_Start config Mapper.Initialize(cfg => { cfg.CreateMap<BlogPost, BlogPost>() .ForMember(p => p.Id, opts => opts.Ignore()); }); Can also decorate model’s Id property with IgnoreMapAttribute but this leaks a dependency to AutoMapper in your models.
  • 46. How to duplicate? BlogPost post = RavenSession.Load<BlogPost>("BlogPosts/1"); BlogPost dupe = Mapper.Map<BlogPost>(post); // dupe.Id == null RavenSession.Store(dupe); // dupe.Id == "BlogPosts/2"
  • 47. Why duplicate? • User Duplicate function (duh) • Auditing/History – Posts/1/History/yyyyMMddHHmmss – Consider Versioning bundle for auditing EVERY document modification • Preview – Posts/1/Preview/ecbe7b49c3de4c8394880bdd81eeae97 • Use Expiration Bundle • Complex Edit/Publish/Cancel – Posts/1/Edit/davidboike • Use Expiration Bundle
  • 48. Duplication: Lessons • Thinking about duplication can help to identify units of change, but can’t get us all the way • Make use of semantic IDs • Duplicating data enables many advanced scenarios • Think about possible document growth • Break down large documents within one unit of change into slimmer documents
  • 49. NServiceBus Saga Storage Hey remember that book I mentioned?
  • 50. NServiceBus/Sagas 101 • NServiceBus – Framework for creating distributed systems – Transactional message handlers w/ auto-retry – Publish/Subscribe • NServiceBus Sagas – Correlated message handlers with shared state – Transactional state stored between messages – Similar in concept to ASP.NET Session State
  • 51. Saga Storage Interface public interface ISagaPersister { T Get<T>(Guid sagaId); T Get<T>(string property, object value); void Save(IContainSagaData saga); void Update(IContainSagaData saga); void Complete(IContainSagaData saga); } public interface IContainSagaData { Guid Id { get; set; } string Originator { get; set; } string OriginalMessageId { get; set; } }
  • 52. Saga Storage History • NServiceBus 2.0 – SQL Storage via Fluent NHibernate • It sucked – Built my own based on XmlSerializer • That also sucked, just not as much • NServiceBus 3.0 – RavenDB Saga Persister became default – Villagers rejoiced!
  • 53. Fluent NHibernate public class MySagaData : IContainSagaData { public virtual int MessagesReceived { get; set; } // Don't touch! NServiceBus uses these internally! public virtual Guid Id { get; set; } public virtual string Originator { get; set; } public virtual string OriginalMessageId { get; set; } } MySagaData Id Originator OriginalMessageId MessagesReceived
  • 54. Fluent NHibernate public class MySagaData : IContainSagaData { public virtual int MessagesReceived { get; set; } // Don't touch! NServiceBus uses these internally! public virtual Guid Id { get; set; } public virtual string Originator { get; set; } public virtual string OriginalMessageId { get; set; } } MySagaData Id Originator OriginalMessageId MessagesReceived
  • 55. Fluent NHibernate public class MySagaData : BaseSagaData { public virtual int MessagesReceived { get; set; } } public abstract class BaseSagaData : IContainSagaData { public virtual Guid Id { get; set; } public virtual string Originator { get; set; } public virtual string OriginalMessageId { get; set; } } MySagaData Id MessagesReceived BaseSagaData Id Originator OriginalMessageId
  • 56. Fluent NHibernate public class MySagaData : IContainSagaData { public virtual List<string> MessageTypesReceived { get; set; } // Don't touch! public virtual Guid Id { get; set; } public virtual string Originator { get; set; } public virtual string OriginalMessageId { get; set; } }
  • 57. Fluent NHibernate public class MySagaData : IContainSagaData { public virtual List<string> MessageTypesReceived { get; set; } // Don't touch! public virtual Guid Id { get; set; } public virtual string Originator { get; set; } public virtual string OriginalMessageId { get; set; } }
  • 58. Fluent NHibernate public class MySagaData : IContainSagaData { public virtual List<ReceivedMessage> MessageTypesReceived { get; set; } // Don't touch! public virtual Guid Id { get; set; } public virtual string Originator { get; set; } public virtual string OriginalMessageId { get; set; } } public class ReceivedMessage { public virtual Guid Id { get; set; } public virtual string MessageTypeReceived { get; set; } } MySagaData Id Originator OriginalMessageId ReceivedMessage Id MySagaDataId MessageTypeReceived
  • 59. Raven Saga Persistence public class MySagaData : ContainSagaData { public List<string> MessageTypesReceived { get; set; } } -OR- public class MySagaData : ContainSagaData { public HashSet<Type> MessageTypesReceived { get; set; } }
  • 60. Back to ISagaPersister public interface ISagaPersister { T Get<T>(Guid sagaId); T Get<T>(string property, object value); void Save(IContainSagaData saga); void Update(IContainSagaData saga); void Complete(IContainSagaData saga); } Need to be able to load a saga by any identifiable property that matches on the incoming message and saga data.
  • 61. More Real-Life Saga Shipping Saga: • Waits for OrderAccepted + OrderBilled events • Publishes OrderShipped event public class ShippingData : ContainSagaData { [Unique] public long OrderId { get; set; } public List<string> MessageTypesReceived { get; set; } } UniqueAttribute • NHibernate persister would create a unique index on the column • RavenDB indexes are asynchronous and NOT consistent – Consistency only guaranteed on Store/Load by ID
  • 62. Pointer Documents // PubSub.Shipping.ShippingData/OrderId/{GUID-1} { "SagaId": "{GUID-2}", "UniqueValue": 12345, "SagaDocId": “ShippingData/{GUID-2}" } // ShippingData/{GUID-2} { "OrderId": 12345, "Originator": "PubSub.Sales@ILM-LION", "OriginalMessageId": "{GUID-3}" }
  • 63. OpenID/OAuth • Login with multiple 3rd-party identities also requires pointer document modeling • OpenID identity URLs make for horrible document IDs – Use the SHA1 hash of the login provider and login identifier in the document ID instead. • Easy way with MVC5 – RavenDB.AspNet.Identity NuGet package – https://github.com/ILMServices/RavenDB.AspNet.Identity – At least look at the source and make fun of the author (That’s me)
  • 64. RavenDB.AspNet.Identity { // ApplicationUsers/DavidBoike (Some boring properties removed) "UserName": "DavidBoike", "Logins": [ { "LoginProvider": "Google", "ProviderKey": "https://www.google.com/accounts/o8/id?id=AItOawnt..." } ] } { // IdentityUserLogins/95c8f50aa725d62af9c7e397ca3a89889dd76514 "UserId": "ApplicationUsers/DavidBoike", "Provider": "Google", "ProviderKey": "https://www.google.com/accounts/o8/id?id=AItOawnt..." }
  • 65. NServiceBus: Lessons • Document databases are ideal for storing random objects with little friction • Unique constraints must be treated specially to ensure consistency – Custom pointer documents – Unique Constraints bundle for more generic functionality (though I prefer not to overuse it) • Use RavenDB.AspNet.Identity for OpenID – Better yet send me a pull request!
  • 66. Final Thoughts Document modeling is not the same as SQL. If you try to make it so, you will fail. So don’t be that guy.
  • 67. Final Thoughts Above all else, think about how and why data changes. – Units of Change – Transactional Boundaries – Everything else is window dressing
  • 68. Final Thoughts Like relational modeling, document modeling is a skill that is acquired through experience – Model things like you would in the real world – Don’t be afraid of trying and failing – Create a /FakeData/Create action in your site to bootstrap your database to make this easy – Especially in the early stages of an application, experiment often and regenerate when needed