A .Net Data library for a DDD-inspired Document DB over Sql
~Sql is a .Net library designed to support a DDD domain model that uses a relational database (namely SQL Server) to provide a transactional document database.
It's goal is to enable storage of a domain model that uses the full expressiveness of C# in a persistence agnostic way.
To get started, add the following packages from Nuget:
dotnet add package TildeSql
dotnet add package TildeSql.SqlServer
dotnet add package TildeSql.JsonNet
If you want to build the solution (and run the tests) you will need Sql Server and dotnet, that's it!
git clone https://github.com/AbstractLeap/tildesql.git
cd tildesql/
What can it do?
- Supports DDD tactical patterns (Entity, Value Object, Service, Aggregate, Repository, Domain Events)
- Removes/minimises primitive obsession
- Complete persistence ignorance -> Automatic dirty tracking
- Migrations
- Unit of Work/Identity Map
- In-memory and distributed caching
- Strongly typed and multiple primary keys
- Batched queries through "Futures"
- Supports persisting generic types
- Multiple tables per type
- Optimistic concurrency
- Inheritance
- Computed and projected columns
- Async everywhere
The following code illustrates the basic operations in ~Sql:
var blogId1 = new BlogId(); // strongly typed key
var blogId2 = new BlogId();
await using var session = this.GetSession(); // async everywhere
var entitiesEnumerable = session.Get<Blog>().MultipleFuture(new [] { blogId1, blogId2 }); // batch up multiple query
var entity = await session.Get<Blog>().SingleAsync(blogId1);
await foreach (var asyncEntity in entitiesEnumerable) { } // async enumeration
var blog = new Blog("My blog");
session.Add(blog);
session.Delete(blog);
var queryEntities = await session.Get<Blog>()
.Where("AuthorId = @AuthorId", new { AuthorId = authorId.Id })
.ToListAsync();
entity.Title = "Foo"; // automatic dirty checking
await session.SaveChangesAsync();
The following example illustrates how an inheritance hierarchy could be stored in a single table with the identity map ensuring that a query for a super type still returns the original (more specifically typed) instance for the same session:
var poodle = new Poodle("Paul");
session.Add(poodle);
var dog = await session.Get<Dog>().SingleAsync(poodle.Id);
Assert.Same(poodle, dog);
This example shows the storage of a generic type:
var thing = new Entity<Foo>(new Foo { Name = "Foofoo" });
insertSession.Add(thing);
await insertSession.SaveChangesAsync();
var selectSession = sf.StartSession();
var personAgain = await selectSession.Get<Entity<Foo>>().SingleAsync(thing.Id);
Assert.Equal("Foofoo", personAgain.Thing.Name);
If you'd like to contribute, please fork the repository and use a feature branch. Pull requests are warmly welcome.
You may want to open an issue in the repository first :-)
"The code in this project is licensed under MIT license."