Что нам стоит dal построить? Акуляков Артём d2d just.net

Post on 16-Apr-2017

466 Views

Category:

Software

8 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Что нам стоит 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

спасибо за внимание

akulyakov.artem@gmail.comhttp://github.com/0xffaahttp://vk.com/oxffaa

top related