Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
-
Upload
dev2devconf -
Category
Software
-
view
459 -
download
8
Transcript of Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
Что нам стоит DAL построить?
Акуляков Артём
кто я естьdotnet в основномpython в свободное времяfunctional programming еще
докладчик dotnetconf, d2dорганизатор d2d
2
в чем проблема?
3
dal
data access layer
4
dal
data access layer● persistence ignorance для bl
5
dal
data access layer● persistence ignorance для bl● обработка данных это ключевая функция
программы
6
dal
data access layer● persistence ignorance для bl● обработка данных это ключевая функция
программы● большая часть проблем с
производительностью связана с доступом к данным
7
противоречие
8
как построить dal?
9
repository
10
repository v1public interface IRepository<TEntity>
where TEntity: class
{
IEnumerable<TEntity> GetAll();
TEntity GetById(int id);
void Add(TEntity obj);
void Update(TEntity obj);
void Remove(TEntity obj);
}
11
repository v1public interface IPostsRepository : IRepository<Post>
{
Post GetByTitle(string title);
}
12
repository v1 # проблемы
● разрастание интерфейса
13
repository v1public interface IPostsRepository : IRepository<Post>
{
Post GetByTitle(string title);
IEnumerable<Post> SearchByContent(string text);
IEnumerable<Post> GetOnlyWithComments();
… +100500 методов
}
14
repository v1 # проблемы
● разрастание интерфейса● разрастание зависимостей● разрастание ответственности
15
repository v1public PostsRepository(IDbContext context,
ISearchEngine searchEngine,
ITokenizer tokenizer,
IPostComparator comparator,
ITransactionManager transactionManager,
… +100500 зависимостей)
{
…
}
16
что делать с операциями чтения?
17
IQueryable<T>
18
repository v2public interface IRepository<TEntity>
where TEntity : class
{
IQueryable<TEntity> Query();
void Add(TEntity obj);
void Update(TEntity obj);
void Remove(TEntity obj);
}
19
repository v2public IEnumerable<Post> GetActivePosts() {
return _repository.Query< Post>()
.Where(p => !p.IsDeleted)
.Include(p => p.Author).ToArray();
}
public IEnumerable<Post> GetTopPosts() {
return _repository.Query< Post>()
.Where(p => !p.IsDeleted)
.Where(p => p.Comments.Count() > 50)
.Include(p => p.Author).ToArray();
}
20
repository v2 # проблемы
● дублирование запросов
21
repository v2public IEnumerable<Post> GetActivePosts() {
return _repository.Query< Post>()
.Where(p => !p.IsDeleted)
.Include(p => p.Author).ToArray();
}
public IEnumerable<Post> GetTopPosts() {
return _repository.Query< Post>()
.Where(p => !p.IsDeleted)
.Where(p => p.Comments.Count() > 50)
.Include(p => p.Author).ToArray();
}
22
repository v2 # проблемы
● дублирование запросов● дублирование подгрузки
23
repository v2public IEnumerable<Post> GetActivePosts() {
return _repository.Query< Post>()
.Where(p => !p.IsDeleted)
.Include(p => p.Author).ToArray();
}
public IEnumerable<Post> GetTopPosts() {
return _repository.Query< Post>()
.Where(p => !p.IsDeleted)
.Where(p => p.Comments.Count() > 50)
.Include(p => p.Author).ToArray();
}
24
include
- EntityFramework- System.Data.Entity
- QueryableExtensions- Include(...) + 2 overloads
25
repository v2 # проблемы
● дублирование запросов● дублирование подгрузки● include
26
specification
27
specificationpublic interface ISpecification<TEntity>
where TEntity : class
{
Expression<Func<TEntity, bool>> Filter { get; }
}
28
specificationpublic class NotDeleted : ISpecification<Post>
{
public Expression<Func<Post, bool>> Filter
{
get { return x => x.IsDeleted == false; }
}
}
29
repository v2 # уже лучшеpublic IEnumerable<Post> GetActivePosts() {
return _repository.Query< Post>()
.Where(_isActiveSpecification.Filter)
.Include(p => p.Author).ToArray();
}
public IEnumerable<Post> GetTopPosts() {
return _repository.Query< Post>()
.Where(_isActiveSpecification.Filter)
.Where(p => p.Comments.Count() > 50)
.Include(p => p.Author).ToArray();
}
30
немного “сахара”public static IQueryable<TEntity>
Apply<TEntity>(this IQueryable<TEntity> query,
ISpecification<TEntity> spec)
where TEntity : class
{
return query.Where(spec.Filter);
}
31
repository v2 # еще лучшеpublic IEnumerable<Post> GetActivePosts() {
return _repository.Query< Post>()
.Apply(_isActiveSpecification)
.Include(p => p.Author).ToArray();
}
public IEnumerable<Post> GetTopPosts() {
return _repository.Query< Post>()
.Apply(_isActiveSpecification)
.Where(p => p.Comments.Count() > 50)
.Include(p => p.Author).ToArray();
}
32
repository v2 # проблемы
● дублирование запросов● дублирование подгрузки● include
33
collector
34
collectorpublic interface ICollector<TEntity>
where TEntity : class
{
IEnumerable<Expression<Func<TEntity, object>>> Includes
{
get;
}
}
35
collectorpublic class PostCollector : ICollector<Post>
{
public IEnumerable<Expression<Func<Post, object>>> Includes
{
get
{
yield return p => p.Author;
yield return p => p.Comments;
}
}
}
36
как применить collector с repository?
37
collectorpublic interface IRepository<TEntity>
where TEntity : class;
{
IQueryable<TEntity> Query(
ICollector<TEntity> collector = null)
…
}
38
repository v2 # совсем хорошоpublic IEnumerable<Post> GetTopPosts() {
return _repository
.Query<Post>(_postWithAuthorCollector)
.Apply(_onlyActiveSpecification)
.Where(p => p.Comments.Count() > 50)
.ToArray();
}
39
пример из реальной жизни
40
пример из жизниpublic void BanAuthor(string login) {
var author = _authorsRep.Query().Single(a => a.Login == login);
var posts = _postsRep.Query().Where(p => p.Author.Id == author.Id);
foreach (var post in posts) {
post.IsDeleted = true;
_postsRep.Update(post);
}
author.Karma = 0;
author.IsReadOnly = true;
authorsRepository.Update(author);
} 41
unit of work
42
unit of workpublic interface IUnitOfWork : IDisposable
{
void Commit();
}
43
repositorypublic interface IRepository<TEntity>
where TEntity : class
{
IQueryable<TEntity> Query(
ICollector<TEntity> collector = null);
void Add(TEntity obj);
void Remove(TEntity obj);
}
44
как связать unit of work и repository?
45
unit of work + repository = factorypublic interface IDalFactory
{
IRepository<TEntity> GetRepository<TEntity>()
where TEntity : class;
IUnitOfWork GetUnitOfWork();
}
46
unit of workusing (var uow = _dalFactory.GetUnitOfWork()){
var postsRep = _dalFactory.GetRepository< Post>();
var authorsRep = _dalFactory.GetRepository< Author>();
var author = authorsRep.Query().Single(a => a.Login == login);
var posts = postsRep.Query().Where(p => p.Author.Id == author.Id);
foreach (var post in posts) post.IsDeleted = true;
author.Karma = 0;
author.IsReadOnly = true;
uow.Commit();
} 47
profit?
48
итог
● repository● unit of work● factory● specifications● collectors
49
проблемы
● IQueryable<T> - дырявая абстракция
50
проблемы
● IQueryable<T> - дырявая абстракция● cложно
51
проблемы
● IQueryable<T> - дырявая абстракция● cложно● много абстракций
52
как избавиться от этих недостатков?
53
размышления
● cRud
54
размышления
● cRud● масштабирование
55
масштабирование # как?
● индексы & оптимизация
56
масштабирование # как?
● индексы & оптимизация● просто sql & Dapper.NET
57
масштабирование # как?
● индексы & оптимизация● просто sql & Dapper.NET● кластер
58
масштабирование # как?
● индексы & оптимизация● просто sql & Dapper.NET● кластер● совсем не много денормализации
- много денормализации- очень много денормализации
59
масштабирование # как?
● индексы & оптимизация● просто sql & Dapper.NET● кластер● совсем не много денормализации
- много денормализации- очень много денормализации
● NoSQL- MongoDB, Redis, RavenDB, RethinkDB
60
как спроектировать dal устойчивый к масштабированию?
61
нужно чуть больше супер силы
62
command-query responsibility segregation
63
cqrs
64
command-query
dispatcher handler
cqrs
● commands & query
65
cqrspublic class PublishPostCommand : IValidatable {
public Guid PostUid { get; set; }
public DateTime PublishDate { get; set; }
…
}
public class PostsByDateQuery : IValidatable {
public DateTime PublicationDate { get; set; }
…
}66
cqrs
● commands & queries● handlers
67
cqrspublic interface ICommandHandler<in TCommand>
where TCommand : IValidatable
{
void Handle(TCommand command);
}
public interface IQueryHandler<in TQuery,out TResult>
where TQuery : IValidatable
{
TResult Handle(TQuery query);
}68
cqrs # примерpublic class PostByDateQueryHandler :
IQueryHandler<PostsByDateQuery, Post>
{
public PostByDateQueryHandler(IDbContext dbContext,
IMongoClient mongoClient)
{…}
public Post Handle(PostsByDateQuery query)
{…}
}69
cqrs # можно реализовать blpublic class PostByDateQueryHandler :
IQueryHandler<PostsByDateQuery, Post>
{
public PostByDateQueryHandler(
IRepository<Post> dbContext,
IFullPostCollector collector)
{…}
public Post Handle(PostsByDateQuery query)
{…}
} 70
cqrs
● commands & queries● handlers● dispatcher
71
cqrspublic interface IDispatcher
{
void Dispatch<TCommand>(TCommand command)
where TCommand: IValidatable;
TResult Dispatch<TQuery, TResult>(TQuery query)
where TQuery : IValidatable;
}
72
cqrs # dispatcher
● ioc container● message bus● conventions● …
73
cqrs # dispatcherpublic class Dispatcher : IDispatcher {
…
public Dispatcher(IComponentContext componentContext)
{ _componentContext = componentContext; }
public void Dispatch<TCommand>(TCommand command)
where TCommand : IValidatable {
_componentContext
.Resolve<ICommandHandler<TCommand>>()
.Handle(command);
}
…
} 74
cqrs # используемpublic Post ReadPost()
{
var query = new PostsByDateQuery
{
PublicationDate = DateTime.Now
};
return _dispatcher
.Dispatch<PostsByDateQuery, Post>(query);
}75
cqrs
76
MyApp.Domain MyApp.DAL
Entites, Services, Managers …
CommandsQueriesIDispatcher
IQueryHandlerICommandHandlerCommandHandlersQueryHandlersDispatcher
а что про масштабирование?
77
cqrs # масштабирование
78
db
ui
domain dalcommands
dal : thin query modelqueries
cqrs # масштабирование
79
writedb
ui
domain dalcommands
dal : thin query modelqueries
readdb
cqrs # масштабирование
80
writedb
ui
domain dalcommands
dal : thin query modelqueries
readdb
cqrs # достоинства
● очень чистый домен
81
cqrs # достоинства
● очень чистый домен● сложность системы растет линейно
82
cqrs # достоинства
● очень чистый домен● сложность системы растет линейно● легко использовать специфичные
функции бд
83
cqrs # недостатки
● crud делать неудобно
84
cqrs # недостатки
● crud делать неудобно● консистентность данных
85
cqrs # недостатки
● crud делать неудобно● консистентность данных● мало кто знает как готовить правильно
86
почитать- http://cqrs.nu/- http://martinfowler.com/bliki/CQRS.html- http://blog.byndyu.ru/2014/07/command-and-query-responsibility.html- http://blog.byndyu.ru/2014/05/blog-post.html- http://blog.byndyu.ru/2013/03/dapper-queryobject-orm.html- http://blog.byndyu.ru/2011/08/repository.html- http://blog.byndyu.ru/2011/01/domain-driven-design-repository.html- http://martinfowler.com/eaaCatalog/repository.html- http://martinfowler.com/eaaCatalog/unitOfWork.html- http://martinfowler.com/bliki/SpecificationByExample.html- http://martinfowler.com/apsupp/spec.pdf- http://blog.ploeh.dk/2012/03/26/IQueryableTisTightCoupling/
87
хорошая архитектура по - это баланс между гибкостью и сложностью
88
спасибо за внимание
[email protected]://github.com/0xffaahttp://vk.com/oxffaa