Работа с данными - это ключевая функция большинства приложений. Но работать с данными не так просто как кажется. С одной стороны, нам нужна производительность, с другой все best practices диктуют нам принцип persistence ignorance, с третьей еще и хочется писать красивый и понятный код. Как найти баланс между всем этим? Чем хороший IRepository отличается от плохого? Что такое CQRS и причем тут функциональное программирование? Об этом и пойдет речь, а так же немного граблей и личного опыта.
6. dal
data access layer
● persistence ignorance для bl
● обработка данных это ключевая функция
программы
6
7. dal
data access layer
● persistence ignorance для bl
● обработка данных это ключевая функция
программы
● большая часть проблем с
производительностью связана с доступом
к данным
7
29. specification
public class NotDeleted : ISpecification<Post>
{
public Expression<Func<Post, bool>> Filter
{
get { return x => x.IsDeleted == false; }
}
}
29
30. 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
31. немного “сахара”
public static IQueryable<TEntity>
Apply<TEntity>(this IQueryable<TEntity> query,
ISpecification<TEntity> spec)
where TEntity : class
{
return query.Where(spec.Filter);
}
31
32. 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
33. repository v2 # проблемы
● дублирование запросов
● дублирование подгрузки
● include
33
36. collector
public class PostCollector : ICollector<Post>
{
public IEnumerable<Expression<Func<Post, object>>> Includes
{
get
{
yield return p => p.Author;
yield return p => p.Comments;
}
}
}
36
46. unit of work + repository = factory
public interface IDalFactory
{
IRepository<TEntity> GetRepository<TEntity>()
where TEntity : class;
IUnitOfWork GetUnitOfWork();
}
46
47. unit of work
using (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
59. масштабирование # как?
● индексы & оптимизация
● просто sql & Dapper.NET
● кластер
● совсем не много денормализации
- много денормализации
- очень много денормализации
59
60. масштабирование # как?
● индексы & оптимизация
● просто sql & Dapper.NET
● кластер
● совсем не много денормализации
- много денормализации
- очень много денормализации
● NoSQL
- MongoDB, Redis, RavenDB, RethinkDB 60
68. cqrs
public 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
69. cqrs # пример
public class PostByDateQueryHandler :
IQueryHandler<PostsByDateQuery, Post>
{
public PostByDateQueryHandler(IDbContext dbContext,
IMongoClient mongoClient)
{…}
public Post Handle(PostsByDateQuery query)
{…}
}
69
70. cqrs # можно реализовать bl
public class PostByDateQueryHandler :
IQueryHandler<PostsByDateQuery, Post>
{
public PostByDateQueryHandler(
IRepository<Post> dbContext,
IFullPostCollector collector)
{…}
public Post Handle(PostsByDateQuery query)
{…}
} 70