Projektowanie obiektowe oprogramowania Wzorce architektury aplikacji (3) Wykład 11 Repository, Unit of Work Wiktor Zychla 2017

Podobne dokumenty
Projektowanie obiektowe oprogramowania Wzorce architektury aplikacji (3) Wykład 11 Repository, Unit of Work Wiktor Zychla 2016

Projektowanie obiektowe oprogramowania Wykład 9 Wzorce architektury aplikacji (1) Wiktor Zychla 2013

Projektowanie obiektowe oprogramowania Wykład 9 Wzorce architektury aplikacji (1) Wiktor Zychla 2014

Projektowanie obiektowe oprogramowania Wykład 4 wzorce projektowe cz.i. wzorce podstawowe i kreacyjne Wiktor Zychla 2017

Projektowanie obiektowe oprogramowania Wzorce architektury aplikacji (2) Wykład 10 Inversion of Control Wiktor Zychla 2013

Projektowanie obiektowe oprogramowania Wykład 9 Wzorce architektury aplikacji (1) Wiktor Zychla 2015

Poznaj ASP.NET MVC. Kamil Cieślak Microsoft Student Partner

Mapowanie obiektowo-relacyjne z wykorzystaniem Hibernate

Projektowanie aplikacji z bazami danych

Projektowanie obiektowe oprogramowania Wykład 4 wzorce projektowe cz.i. wzorce podstawowe i kreacyjne Wiktor Zychla 2015

Programowanie obiektowe

Programowanie obiektowe i zdarzeniowe wykład 4 Kompozycja, kolekcje, wiązanie danych

Przykładowa implementacja

Warstwa integracji. wg. D.Alur, J.Crupi, D. Malks, Core J2EE. Wzorce projektowe.

Programowanie obiektowe

Projektowanie obiektowe oprogramowania Testowanie oprogramowania Wykład 13 Wiktor Zychla 2014

Zaawansowane programowanie w C++ (PCP)

Dzisiejszy wykład. Wzorce projektowe. Visitor Client-Server Factory Singleton

Plik pobrano z Tytuł: Wzorce projektowe, cz. 2 Strategy Ostatnia aktualizacja:

Instrukcja tworzenia aplikacji EE na bazie aplikacji prezentowanej na zajęciach lab.4 z PIO umożliwiająca przez sieć dostęp wielu użytkownikom.

Informacje wstępne Autor Zofia Kruczkiewicz Wzorce oprogramowania 4

Projektowanie obiektowe oprogramowania Wykład 5 wzorce strukturalne Wiktor Zychla 2016


Wzorce logiki dziedziny

Analiza i projektowanie obiektowe 2016/2017. Wykład 11: Zaawansowane wzorce projektowe (1)

Java Persistence API - zagadnienia zaawansowane

Builder (budowniczy) Cel: Przykład:

MVVM Light Toolkit. Julita Borkowska

Projektowanie obiektowe. Roman Simiński Wzorce projektowe Wybrane wzorce strukturalne

Baza danych sql. 1. Wprowadzenie. 2. Repozytaria generyczne

Baza danych sql. 1. Wprowadzenie

Aplikacje RMI

Omówienie wzorców wykorzystywanych w Prism 5.0. Dominika Różycka

Interfejsy. Programowanie obiektowe. Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej

Podejście obiektowe do relacyjnych baz danych Hibernate.

Technologie obiektowe

Kurs programowania aplikacji bazodanowych

1 LINQ. Zaawansowane programowanie internetowe Instrukcja nr 1

Projektowanie obiektowe Wzorce projektowe. Gang of Four Strukturalne wzorce projektowe (Wzorce interfejsów)

Grzegorz Ruciński. Warszawska Wyższa Szkoła Informatyki Promotor dr inż. Paweł Figat

Spring Framework - wprowadzenie i zagadnienia zaawansowane

Projektowanie obiektowe oprogramowania Wzorce architektury aplikacji (2) Wykład 10 Inversion of Control/Dependency Injection Wiktor Zychla 2014

Różne odmiany budowniczego. Po co używać wzorca budowniczy? Kiedy używać budowniczego

Projektowanie obiektowe oprogramowania Wykład 7 wzorce czynnościowe (2) Wiktor Zychla 2018

Diagram wdrożenia. Rys. 5.1 Diagram wdrożenia.

Wzorce dystrybucji i wspólbieżności autonomicznej

Problemy projektowania obiektowego. Czy podobne problemy można rozwiązywac w podobny sposób?

Programowanie obiektowe

Dokumentacja do API Javy.

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Programowanie obiektowe i zdarzeniowe

Aplikacja webowa w Javie szybkie programowanie biznesowych aplikacji Spring Boot + Vaadin

Wprowadzenie do projektu QualitySpy

Zwinna współpraca programistów i testerów z wykorzystaniem BDD i. by Example (JBehave/Spock/SpecFlow)

Projektowanie oprogramowania. Warstwa integracji z bazą danych oparta na technologii ORM Platforma Java EE Autor: Zofia Kruczkiewicz

Instrukcja laboratoryjna

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Zadanie polega na stworzeniu bazy danych w pamięci zapewniającej efektywny dostęp do danych baza osób.

Wprowadzenie do programowania aplikacji mobilnych

Pico. Wstęp do kontenerów IoC.

Programowanie komponentowe 5

Wykład 7: Pakiety i Interfejsy

Programowanie bez fabryki. Po co używać wzorca fabryki?

Wprowadzenie db4o - podstawy db4o - technikalia Przydatne wiadomości. Wprowadzenie. db4o. Norbert Potocki. 1 czerwca Norbert Potocki db4o

Wzorce Strukturalne. Adapter: opis. Tomasz Borzyszkowski

Wywoływanie metod zdalnych

Programowanie obiektowe

Projektowanie obiektowe oprogramowania Wzorce architektury aplikacji (2) Wykład 10 Inversion of Control/Dependency Injection Wiktor Zychla 2016

Remote Method Invocation 17 listopada Dariusz Wawrzyniak (IIPP) 1

Języki i metody programowania Java. Wykład 2 (część 2)

PHP 5 język obiektowy

Singleton. Cel: Przykład: Zastosowanie: Zapewnienie, że klasa ma tylko jedną instancję i dostarczenie globalnego dostępu do niej.

Projekt INP Instrukcja 2. Autor Dr inż. Zofia Kruczkiewicz

Podejście obiektowe do budowy systemów rozproszonych

Projektowanie obiektowe oprogramowania Wzorce architektury aplikacji (2) Wykład 10 Inversion of Control/Dependency Injection Wiktor Zychla 2018

Szablony funkcji i klas (templates)

Java RMI. Dariusz Wawrzyniak 1. Podejście obiektowe do budowy systemów rozproszonych. obiekt. interfejs. kliencka. sieć

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

xmlns:prism= c. <ContentControl prism:regionmanager.regionname="mainregion" />

Wzorce projektowe. dr inż. Marcin Pietroo

Db4o obiektowa baza danych wersja.net

Bazy danych dla producenta mebli tapicerowanych. Bartosz Janiak Marcin Sikora Wrocław r.

Wywoływanie metod zdalnych

UML a kod w C++ i Javie. Przypadki użycia. Diagramy klas. Klasy użytkowników i wykorzystywane funkcje. Związki pomiędzy przypadkami.

Diagram klas UML jest statycznym diagramem, przedstawiającym strukturę aplikacji bądź systemu w paradygmacie programowania obiektowego.

PWSG Ćwiczenia 12. Wszystkie ukończone zadania należy wysłać na adres: lub

Modelowanie hierarchicznych struktur w relacyjnych bazach danych

Prototype (prototyp) Cel: Przykład: Określenie rodzaju tworzonych obiektów poprzez wskazanie ich prototypu. Nowe instancje tworzymy kopiując prototyp.

Programowanie w języku Java WYKŁAD

Wzorce projektowe warstwy danych

Laboratorium z przedmiotu Programowanie obiektowe - zestaw 04

Budowanie aplikacji biznesowych przy użyciu. Presentation Foundation i wzorca MVVM

Wykład 8: klasy cz. 4

Remote Method Invocation 17 listopada 2010

JPA Java Persistance API

Aplikacje RMI Lab4

Programowanie obiektowe

NHibernate. Narzędzie mapowania obiektowo - relacyjnego

Transkrypt:

Projektowanie obiektowe oprogramowania Wzorce architektury aplikacji (3) Wykład 11 Repository, Unit of Work Wiktor Zychla 2017 Repository dodatkowa warstwa abstrakcji na obiektową warstwę dostępu do danych. Zwykle Repository kontroluje dostęp do jednej kategorii danych (np. jednej tabeli z bazy danych) Unit of Work kompozyt wielu repozytoriów zarządza ich czasem życia i pozwala na dostęp do nich z jednego miejsca. Dodatkowo bierze na siebie m.in. zarządzanie transakcjami. 1 Repository Zalety wprowadzenia repozytorium jako warstwy abstrakcji: Uniezależnienie warstwy przetwarzania danych (logika biznesowa) od implementacji warstwy dostępu do danych wraz ze zmieniającymi się technologiami można łatwo dostarczać nowych, wydajniejszych implementacji repository, używających zupełnie innych technologii dostępu do danych (niekoniecznie nawet relacyjnych baz danych! można wyobrazić sobie implementacje Repository które odwołują się do baz nierelacyjnych, do usług katalogowych, są implementacje które do baz relacyjnych dostają się za pomocą jakiegoś silnika ORM i są też takie które używają niskopoziomowego interfejsu typu ADO.NET do dostępu do danych) Umożliwienie łatwego zastępowania implementacji repozytorium testy jednostkowe warstw przyległych bez efektów ubocznych dzięki implementacjom typu fake/stub. Wady wprowadzenia repozytorium jako warstwy abstrakcji: Dodatkowa warstwa w architekturze aplikacji Kontrowersje wokół wzorcowego interfejsu jaki powinno implementować repozytorium (generic repository vs concrete repository?) Jeżeli technologia ORM sama z siebie jest już zaprojektowana według wzorców Repo/UoW, to dodatkowe jej opakowywanie może być dyskusyjne Dla zadanych klas modelu public class User public class Account przykładowy interfejs tzw. generycznego repozytorium (generic repository):

public interface GenericRepository<T> T New(); void Insert( T item ); void Update( T item ); void Delete( T item ); IQueryable<T> Query ; i jego implementacja dla jednej z klas: public class UserRepository : GenericRepository<User> T GenericRepository<User>.New() void GenericRepository<User>.Insert( User item ) void GenericRepository<User>.Update( User item ) void GenericRepository<User>.Delete( User item ) IQueryable<User> GenericRepository<User>.Query Alternatywą dla generic repository jest concrete repository : public interface ConcreteUserRepository IEnumerable<User> RetrieveAllUsers(); User RetrieveSingle( int Id ); IEnumerable<User> FindAllUsersForStartingLetter( string FirstSurnameLetter ); User New(); void Insert( User item ); void Update( User item ); void Delete( User item );

Porównanie generic repository i concrete repository : Concrete Repository Wymaga się tylu różnych konkretnych interfejsów repozytoriów, ile jest klas w modelu dziedzinowym Każdy interfejs udostępnia metody dostępu do danych specyficzne dla konkretnego typu (na przykład użytkowników będziemy wyszukiwać według rozbudowanych kryteriów (i każde kryterium może być osobną metodą w kontrakcie repozytorium) Zaprojektowanie i utrzymanie interfejsów repozytoriów w dużym projekcie jest trudne i żmudne Generic Repository Jest tylko jeden, wspólny, generyczny interfejs repozytorium, który ma wiele implementacji jest to możliwe dlatego, że wszystkie możliwe skomplikowane warianty zapytań zamyka tu kontrakt LINQ (czyli zwracanie klientowi obiektu implementującego IQueryable) (w innych technologiach zapytań mógłby to być inny uniwersalny interfejs zapytań np. JPA, HQL itp). Problemem generycznego repozytorium jest to, że różne technologie w różny sposób implementują uniwersalne języki zapytań (LINQ/JPA/ ), w szczególności wyrażenia mogą być poprawnie interpretowane w pewnych implementacjach a w innych nie. I nagle klient może się przekonać że dostarczona mu implementacja A jest zbyt uboga żeby wykonać konkretne zapytanie, gdy tymczasem implementacja B radzi sobie z tym dobrze. To łamie zasadę, w której to dostawca implementacji musi odpowiadać za realizację kontraktu, a nie klient być zmuszonym do posiadania wiedzy o stanie implementacji kontraktu przez różne możliwe implementacje. 2 Unit of Work Unit of Work jest kompozytem wielu repozytoriów: public interface IUnitOfWork GenericRepository<IUser> UserRepository ; GenericRepository<IAddress> AddressRepository ; void SaveChanges(); // opcjonalne void BeginTransaction(); void CommitTransaction(); void RollbackTransaction(); Można powiedzieć w uproszczeniu, że o ile Repository jest abstrakcją dostępu do pojedynczej tabeli w bazie relacyjnej, to Unit of Work jest dostępem do wszystkich repozytoriów z jednego miejsca, czyli do wszystkich tabel. Klient zawsze korzysta z instancji Unit of Work tworząc sesję dostępu do danych. W ramach jednego Unit of Work poszczególne repozytoria współdzielą ten sam kontekst (czyli jakieś niskopoziomowe szczegóły implementacji, połączenie do bazy danych, uchwyty itp.) Interfejs Unit Of Work może dodatkowo przewidywać m.in. zarządzanie transakcjami czy metadanymi bazy danych.

Uwaga! Bywa, że wzorzec Repository jest opisywany w oderwaniu od Unit of Work, a co za tym idzie tak też bywa implementowany (repozytoria bez Unit of Work). Jest to podejście błędne i rodzi niepotrzebne trudności przy przekazywaniu repozytoriów do wyższych warstw aplikacji. 3 Abstrakcje klas modeli Podczas wykładu zobaczymy przykład na żywo budowania warstwy repozytorium dla dwóch przykładowych technologii mapowania obiektowo-relacyjnego: Linq2SQL i Entity Framework. Zasadnicza różnica między tymi technologiami polega na sposobie implementacji klas modeli (patrz notatki do wykładu o mapowaniu obiektowo-relacyjnym) i właściwości nawigacyjnych w nich (navigation properties): w przypadku Linq2SQL mamy do czynienia z implementacją typu Value Holder. Model jest generowany przez automat, klasy zawierają wygenerowany kod właściwości nawigacyjnych, którego nie można modyfikować w przypadku EF mamy do czynienia z implementacją typu Virtual Proxy. Model jest budowany ręcznie i oparty na klasach typu POCO. Wymaganie jakie sobie stawiamy jest takie, że użyjemy kontenera IoC do konfiguracji wybranej implementacji i chcemy aby kod klienta (warstwy logiki biznesowej) w ogóle nie zmieniał się przy wymianie warstwy dostępu do danych. Podstawowa trudność jaka pojawia się przy takim wymaganiu związana jest właśnie ze sposobem implementacji właściwości nawigacyjnych. Skoro raz właściwości nawigacyjne implementuje automat i są one jawnie zaimplementowane w kodzie (Linq2SQL) a innym razem implementuje je generator proxy, to oznacza że potrzeba dwóch różnych typów modeli, każdemu z podejść odpowiada bowiem inny model. A to uniemożliwia spełnienie wymagania o niemodyfikowaniu kodu klienckiego przy wymianie repozytorium na inne repozytorium. Jak rozwiązać ten problem? Literatura sugeruje żeby posłużyć się wzorcem ViewModel (patrz Mark Seemann, Dependency Injection in.net ). W podejściu tym mamy jeden uniwersalny model, niezwiązany z modelem utrwalanym (persistence model) czyli pozwalamy każdej technologii mapowania obiektowo relacyjnego mieć swój własny zestaw klas modelu, a dodatkowo mamy ten jeden uniwersalny model i konwersje z niego do każdego z konkretnych modeli utrwalanych. Okazuje się, że to nie jest dobry pomysł z uwagi na brak możliwości implementacji leniwych zależności typu parent-child w klasach tego uniwersalnego modelu. Wydaje się to również nieefektywne z uwagi na konieczność ciągłych konwersji z i do modelu uniwersalnego. Na wykładzie pokażemy, że lepszym podejściem jest opisanie modelu przez interfejsy klas, a następnie implementacji repozytoriów względem interfejsów klas modelu dziedzinowego. Inne podejście to ograniczenie się wyłącznie do tych implementacji technologii dostępu do danych, które potrafią pracować na wskazanym modelu obiektowym typu POCO/POJO (Code First).

4 Organizacja struktury repozytoriów i units of work W świetle powyższych rozważań, właściwa struktura projektu powinna wyglądać następująco: Pakiet/zestaw określający abstrakcje (kontrakty) dla kodu klienckiego: Klasy modeli jeden zbiór abstrakcji (interfejsów) opisujący modele (IUser, IAddress). public interface IParent int ID ; set; string ParentName ; set; IEnumerable<IChild> Children ; set; public interface IChild int ID ; set; int ID_PARENT ; set; string ChildName ; set; IParent Parent ; set; Jeden zestaw abstrakcji repozytoriów w zależności od wyboru jeden generyczny interfejs (IGenericRepository<T>) lub interfejsy szczegółowe (IUserRepository, IAddressRepository) Jedna abstrakcja Unit of Work public interface IGenericRepository<T> T New(); void Insert(T t); void Update(T t); void Delete(T t); IQueryable<T> Query ; public interface IUnitOfWork IGenericRepository<IParent> Parent ; IGenericRepository<IChild> Child ; Local Factory jako API dostępu do instancji UoW public class UnitOfWorkFactory private static Func<IUnitOfWork> _provider; public static void SetProvider( Func<IUnitOfWork> provider ) _provider = provider;

public IUnitOfWork Create() return _provider(); Kod kliencki Referencja wyłącznie do zestawu określającego abstrakcje i Local Factory tyle wystarczy żeby pisać kod kliencki static void Main(string[] args) // referencja do uow zawsze Local Factory var uow = new UnitOfWorkFactory().Create(); // dodanie elementu var parent = uow.parent.new(); parent.parentname = "nowy parent"; uow.parent.insert(parent); // zapytanie foreach ( var child in uow.child.query ) Console.WriteLine(child.ChildName); Console.ReadLine(); Pakiety z implementacjami (kod kliencki nie używa ich wprost, są konfigurowane przez Local Factory z Composition Root, tak jak widzieliśmy to na poprzednim wykładzie) Wiele zestawów implementujących abstrakcje modeli, dla każdego typu repozytorium inna implementacja (EFUser, EFAddress, LinqUser, LinqAddress) Wiele implementacji abstrakcji repozytoriów, dla każdego typu repozytorium inna implementacja (EFUserRepository, EFAddressRepository, LinqUserRepository, LinqAddressRepository) Wiele implementacji abstrakcji Unit of Work, dla każdego typu UoW inna implementacja (EFUnitOfWork, LinqUnitOfWork) Na przykład dla Linq2Sql implementacja jest dość oczywista, jedyne trudniejsze miejsce polega na tym, że automatycznie wygenerowane klasy modeli nie implementują interfejsów opisujących abstrakcje modelu (IChild, IParent), dlatego trzeba dodatkowym kodem podpowiedzieć że jednak implementują. public class Linq2SqlChildRepository : IGenericRepository<IChild> private ParentChildDataContext _context ; set; public Linq2SqlChildRepository( ParentChildDataContext context ) this._context = context;

public IQueryable<IChild> Query return this._context.childs; public void Delete(IChild t) this._context.childs.deleteonsubmit(t as Child); public void Insert(IChild t) this._context.childs.insertonsubmit(t as Child); public IChild New() return new Child(); public void Update(IChild t) public class Linq2SqlParentRepository : IGenericRepository<IParent> private ParentChildDataContext _context ; set; public Linq2SqlParentRepository(ParentChildDataContext context) this._context = context; public IQueryable<IParent> Query throw new NotImplementedException(); public void Delete(IParent t) this._context.parents.deleteonsubmit(t as Parent); public void Insert(IParent t) this._context.parents.insertonsubmit(t as Parent);

public IParent New() return new Parent(); public void Update(IParent t) public class Linq2SqlUnitOfWork : IUnitOfWork private ParentChildDataContext _context ; set; public Linq2SqlUnitOfWork( ParentChildDataContext context ) this._context = context; public IGenericRepository<IChild> Child return new Linq2SqlChildRepository(this._context); public IGenericRepository<IParent> Parent return new Linq2SqlParentRepository(this._context); public void Dispose() this._context.dispose(); // dodatkowy kod w którym dodaje się implementację abstrakcji modeli // do implementacji wygenerowanej przez automat public partial class Child : IChild IParent IChild.Parent return this.parent; set this.parent = (Parent)value;

public partial class Parent : IParent public IEnumerable<IChild> Children return this.children; set this.children = value; Główny moduł aplikacji Composition Root konfiguracja dostawcy dla Local Factory, czyli metody fabrykującej która będzie wykonywać się za każdym razem kiedy klient zawoła Create private static void CompositionRoot() UnitOfWorkFactory.SetProvider(() => var connectionstring = ConfigurationManager.AppSettings["cs"]; var context = new ParentChildDataContext(connectionString); ); return new Linq2SqlUnitOfWork(context); (opcjonalne) Użycie kontenera IoC do zarządzania mapowaniem abstrakcji na implementacje w sensie: provider dla Local Factory który używa IoC