Projektowanie, Programowanie, Codzienność – BeniaminZaborski.com

26 marca 2015

Messaging, czyli pierwsi będą pierwszymi

Filed under: Codzienne dylematy modelarza — Tagi: , , , , — Beniamin Zaborski @ 22:24

Ostatnio zrobiłem przegląd rozwiązań „messagingowych” dla .NET pod kątem użycia w CQRS + ES. Nie wypadało nie zacząć od „rodzimego” MSMQ. Tu niewiele się zmieniło od mojego ostatniego kontaktu z tą technologią. Ostatecznie moją uwagę najbardziej przykuło jedno rozwiązanie: RabbitMQ. Dlaczego? Zalety: otwarte, darmowe, stosunkowo proste w użyciu, multiplatformowe, z client API dla wielu języków w tym oczywiście i dla C#. I najważniejsze – to działa!;). Z tym client API to może, yyy … nie jest najfajniejsze, ale … od czego jest EasyNetQ?
Kolejna zaleta jest taka, że RabbitMQ jest już trochę na rynku i wydaje się mieć na nim ugruntowaną pozycję. Dla mnie bomba, zobaczymy dalej;).

22 lutego 2014

Zrób to szablonowo

Filed under: Codzienne dylematy modelarza — Tagi: , , , , — Beniamin Zaborski @ 14:23

Z zawodem programisty związane są często prace powtarzalne i nudne. W sumie to mogą one stanowić znaczący ułamek całości. Nie zawsze są to wyzwania:). Po prostu robimy coś rutynowo, szablonowo, po raz n-ty. Oczywiście bezmyślne klepanie może nas kiedyś zgubić bo nie trudno o błąd będąc w stanie transu, szczególnie przed poranną kawą.

Rozwiązaniem przynajmniej części tych problemów są generatory tekstu oparte na szablonach – text templating. Takie hasło programistom aplikacji web-owych zapewne się kojarzy z generowaniem widoków na podstawie szablonów, np. Razor.

Jednak widoki w aplikacji web to nie wszystko co możemy generować. Chciałbym spojrzeć na to zagadnienie dużo szerzej. Efekt naszej pracy to kod źródłowy, który jest przecież tekstem. Czemu nie wykorzystać jakiegoś generatora opartego na szablonach do generowania kodu źródłowego. Szczególne pole do manewru jest w tych obszarach aplikacji które są powtarzalne i nudne? Tu liczę na Waszą fantazję.

Ja np. chciałbym aby jak za dotknięciem magicznej różdżki wygenerował mi się obiekt Repository dla wskazanej encji. Wyobrażam sobie, że raz tworzę szablon i … mam więcej czasu na rozkoszowanie się aromatem świeżo palonej brazylijskiej arabiki 100%:).

Visual Studio dostarcza (a dokładniej to chyba VS SDK) generator oparty na szablonach T4. Ja jednak w tym wpisie pokażę alternatywne rozwiązanie, a w zasadzie dwa, moje ulubione StringTemplate oraz NVelocity.

Zanim przejdę do konkretnego przykładu spójrzmy na poniższy diagram jak to w ogóle działa.

Text templating diagram

gdzie:

Templates – szablony przygotowane zgodnie ze składnią danego generatora.

Variables – zmienne przekazane do silnika które zostaną użyte do zastąpienia ciągów w szablonie.

Text Output – wyjściowy plik generatora.

Text Template Engine – silnik generatora.

Na pierwszy ogień przyjrzyjmy się rozwiązaniu StringTemplate. Instalacja jest łatwa, gdyż istnieje odpowiedni pakiet dla NuGet-a, a zatem:

install-package Antlr4.StringTemplate

Przejdźmy teraz do utworzenia szablonu obiektu typu repository. Powiedzmy, że mój prosty szablon będzie wyglądał tak:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Biz.TemplateApp
{
 public class <entity.Name>Repository : BaseEFRepository\<<entity.Name>>
 {
 public class <entity.Name>Repository(IDbContext context) : base(context) {}
 }
}

Widzimy kawałek kodu przedstawiający prostą klasę i chcemy, aby w odpowiednich miejscach została wstawiona nazwa encji. W tym celu użyliśmy składni <entity.Name>. W ten sposób silnik generatora zastąpi ciąg znaków wartością przekazanej zmiennej. Jak widać zmienną nie musi być prosty string. W tym wypadku przekazujemy całą encję a w szablonie bierzemy z niej tylko nazwę. W związku z tym, że używam typu generycznego stąd znak \ który escape-uje specjalny znak < w szablonie.

Spójrzmy jak wywołać generator:

var customerEntity = new { Name = "Customer" };
string templateContent = File.ReadAllText(@"Templates\EntityRepository.tpl");
Template generator = new Template(templateContent);
generator.Add("entity",customerEntity);
string outputCode = generator.Render();

Najpierw wczytujemy z pliku nasz szablon (treść pokazana na poprzednim listingu). W następnej kolejności instancjonujemy nasz generator i dodajemy zmienną o nazwie „entity” i wartości customerEntity. Na koniec wywołujemy metodę Render generatora i uzyskujemy wynik:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Biz.TemplateApp
{
 public class CustomerRepository : BaseEFRepository<Customer>
 {
   public class CustomerRepository(IDbContext context) : base(context) {}
 }
}

Gotowe! Oczywiście to bardzo prosty przykład, a sam StringTemplate ma dużo większe możliwości jak: szablony grupowe, instrukcje warunkowe w szablonach, formatowanie, agregacja, regiony, itp. Po szczegóły odsyłam do dokumentacji. Jak widać nawet tak trywialny przykład jak przedstawiłem może być użyteczny. Możliwości są spore i w zasadzie ograniczone tylko Waszą fantazją.

Zapytacie co dalej z tym zrobić? Jak zintegrować to z Visual Studio? Rozwiązań jest kilka począwszy od custom tooli, a kończąc na dedykowanym pakiecie VS, ale to już temat na osobny post.

Na koniec dla porównania pokaże analogiczne rozwiązanie oparte na NVelocity. Jest odpowiedni pakiet dla NuGet-a, podobnie jak w poprzednim przypadku, a zatem instalacja jest prosta:

install-package NVelocity

NVelocity używa nieco innej składni szablonów – posiada własny język szablonów VTL. VTL używa referencji, aby zawrzeć w kodzie szablonu dynamiczną treść. Jedną z typów referencji jest zmienna. Wartość tej zmiennej może być przekazana z zewnątrz jak i ustawiona w samym szablonie. Generalna zasada VTL-a to: dyrektywa # oznacza że chcemy coś wykonać w szablonie, a dyrektywa $ że chcemy pobrać wartość. W naszym prostym przykładzie używamy jedynie pobierania wartości zmiennych przekazanych z zewnątrz. Oczywiście nie będę opisywał tu całej składni VTL-a, bo nie ma sensu powielanie dokumentacji.

Tak wyglądałby szablon naszego repository dla NVelocity:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Biz.TemplateApp
{
 public class ${entity.Name}Repository : BaseEFRepository<${entity.Name}>
 {
   public class ${entity.Name}Repository(IDbContext context) : base(context) {}
 }
}

NVelocity ewaluuje zmienne za pomocą ${enity.Name}. A teraz wywołanie generatora:

var customerEntity = new { Name = "Customer" };
Velocity.Init();
VelocityContext velocityContext = new VelocityContext();
string templateContent = File.ReadAllText(@"Templates\EntityRepository.vtp");
velocityContext.Put("entity", customerEntity);
StringBuilder outputCode = new StringBuilder();
Velocity.Evaluate(
   velocityContext,
   new StringWriter(outputCode),
   "repo template",
   new StringReader(templateContent)
 );

Najpierw mamy zainicjowanie NVelocity i utworzenie kontekstu, a następnie przekazanie zmiennych za pomocą metody put i wywołanie generatora za pomocą Evaluate.
NVelocity to port java-owego Velocity na .NET i ma naprawdę spore możliwości. To co tu pokazałem to tylko promil jego możliwości.

5 lutego 2014

Transakcje rozproszone – Wszystko albo nic

Dzisiaj trochę o transakcjach. Wiemy, że transakcja to niepodzielna operacja wykonana na jakimś zasobie lub zasobach. Wiemy, także że transakcję charakteryzują cztery litery (nie, to nie te :)), mianowicie ACID. W języku polskim oznaczają one odpowiednio: Atomowość, Spójność, Izolacja, Trwałość.

Większości z nas pojęcie transakcji kojarzy się przeważnie z bazami danych … właśnie, bazy danych. Celowo na początku napisałem, że transakcja to niepodzielna operacja na jakimś zasobie lub zasobach. Tym zasobem może być baza danych, ale także zupełnie coś innego, np. system plików, serwis WCF, obiekt COM+, itd.

Interesuje mnie aspekt transakcyjności w kontekście liczby zasobów większej niż jeden. Powiedzmy, że moja niepodzielna operacja powinna składać się z: dodania rekordu do tabeli bazy danych i utworzenia pliku w systemie plików. Oznacza to że jeśli któraś z tych dwóch operacji nie powiedzie się całość zostanie wycofana w myśl zasady „wszystko albo nic”. Jeśli kodujemy w .NET przed wersją 2.0, to rzeczywiście możemy mieć lekki problem. Na szczęście w wersji 2.0 frameworka .NET pojawił się nowy mechanizm obsługi transakcji zwany TransactionScope.

Zauważyłem, że TransactionScope jest bardzo powszechnie używany przez programistów .NET. No i nie ma w tym nic złego, ale niestety jest on używany bardzo nieświadomie. Standardowe użycie zna chyba każdy:

using (TransactionScope transaction = new TransactionScope())
{
  SaveDataToDB();
  transaction.Complete();
}

I co tu się dzieje? A no transakcja się dzieje!

Metoda SaveDataToDB zawiera operację zapisu danych do jednej bazy danych. Wszystko działa, ale musimy mieć świadomość, że zadziała się magia i TransactionScope użył tu tzw. Lightweight Transaction Manager (LTM). Praktycznie można powiedzieć, że nasza transakcja przełożyła się bezpośrednio na transakcję bazodanową. Pod kątem wydajności LTM jest zbliżona do natywnej transakcji ADO.NET/bazy danych.

Rozważmy teraz przykład który przytoczyłem na początku, tj.:

using (TransactionScope transaction = new TransactionScope())
{  
  SaveDataToDB();
  CreateFile();
  transaction.Complete();
}

Teraz transakcja obejmuje dwa różne zasoby i znów zadzieje się magia i TransactionScope użyje rozproszonego menadżera transakcji opartego na MSDTC. Trochę teraz skłamałem, gdyż standardowo tworząc plik w .NET np. poprzez File.WriteAllText nie jest obsługiwana transakcyjność. Rozwiązaniem jest zastosowanie zewnętrznej biblioteki do transakcyjnego dostępu do systemu plików NTFS (np. Transactional NTFS Managed Wrapper) lub obsłużenie jej samemu. Obsługa jest dość prosta i wymaga zaimplementowania interfejsu IEnlistmentNotification. Trywialny przykład obsługi mógłby wyglądać tak:

public class TransactionalFileCreator : IEnlistmentNotification
{
  private string path;
  private string content;
  public TransactionalFileCreator(string path, string content)
  {
   this.path = path;
   this.content = content;
   Transaction.Current.EnlistDurable(Guid.NewGuid(), this, EnlistmentOptions.None);
  }
  public void Commit(Enlistment enlistment)
  {
    File.WriteAllText(path, content);
  }
  public void InDoubt(Enlistment enlistment)
  {
    enlistment.Done();
  }
  public void Prepare(PreparingEnlistment preparingEnlistment)
  {
    preparingEnlistment.Prepared();
  }
  public void Rollback(Enlistment enlistment)
  {
    if (File.Exists(path))
      File.Delete(path);
  }
}

Bardzo ważna jest, na powyższym listingu, linia Transaction.Current.EnlistDurable(Guid.NewGuid(), this, EnlistmentOptions.None). To ona jest warunkiem promowania wstępnie zarządzanej transakcji przez LTM do DTC. Według dokumentacji jeśli w ramach transakcji użyjemy co najmniej dwóch zasobów „durable” tj. takich obsługujących promowanie transackji do DTC to takie promowanie się odbędzie. Koszt transakcji zarządzanej przez DTC jest oczywiście znacznie większy niż tej opartej na LTM.

Uruchomienie w transakcji promowanej do DTC naszego kodu wyglądałoby teraz tak:

using (TransactionScope transaction = new TransactionScope())
{  
  SaveDataToDB();
  TransactionalFileCreator fileCreator = new TransactionalFileCreator(path, content);
  var dtIdentifier = Transaction.Current.TransactionInformation.DistributedIdentifier;
  transaction.Complete();
}

Transaction.Current.TransactionInformation.DistributedIdentifier to pobranie identyfikatora transakcji z rozproszonego koordynatora transakcji MSDTC. Naszą transakcję możemy także podejrzeć w „Koordynatorze transakcji rozproszonych” w „Panel sterowania” -> „Usługi składowe” -> „Lokalna usługa DTC” -> „Lista transakcji”:

DTC_tran

Zachęcam do zgłębienia wiedzy na temat TransactionScope, a można zacząć np. tutaj. Zdobycie wiedzy o tym jaki mechanizm zarządza naszą transakcją może nas uratować od niezłych kłopotów.

17 stycznia 2014

Temporal data – czyli w jakim departamencie Pani pracowała w maju 2008?

Dziś chciałbym spojrzeć na dane w nieco innym świetle niż robimy to na co dzień. Nie ważne czy tworzymy duży rozproszony system czy prostą aplikację klient-serwer zawsze pojawia się kontekst danych.  Podstawowym repozytorium danych we współczesnych aplikacjach, szczególnie biznesowych, są relacyjne bazy danych. Jak zwykle postrzegamy te dane? Jak o nich myślimy?

W klasycznym podejściu nie rozpatrujemy danych w kontekście czasu. Załóżmy, że mamy tabelę przechowującą pracowników. Wiemy, że dane pracownika X mogą się zmieniać na przestrzeni tygodni, miesięcy, lat…Co w sytuacji gdy chcielibyśmy nagle cofnąć się w czasie do konkretnego punktu z przeszłości i obejrzeć naszego pracownika? Hmm … no nic! W klasycznym systemie nie możemy czegoś takiego zrobić, ponieważ dane nie posiadają pojęcia czasu. Inaczej mówiąc system przechowuje bieżące dane, które są takie same w każdym punkcie czasu.

Co zatem z tym zrobić? Tutaj pojawia się pojęcie: temporal database. To baza danych w której dane posiadają kontekst czasu. Mówię tutaj o bazach danych, ale pojęcie „czasowości” danych należy postrzegać znacznie szerzej. Na wyższym poziomie abstrakcji także możemy wprowadzić czasowość, tj. np. w obiektach naszego modelu domeny.

Aspekt „czasowości” danych możemy rozpatrywać w co najmniej na dwa sposoby, a zależy to od wymagań naszego systemu. Mianowicie nasze dane mogą posiadać atrybut ValidTime, który wprowadza kontekst czasu w sensie obowiązywania danych w świecie rzeczywistym. Inaczej mówiąc określa on w jakim czasie konkretna wersja naszych danych jest prawdziwa z punktu widzenia świata rzeczywistego.  Drugi sposób to wprowadzenie atrybutu TransactionTime, który również określa dane w czasie, ale interpretacja jest tu nieco inna. To podejście ma zastosowanie gdy interesuje nas raczej czas rejestracji danych w systemie, ale nie moment zaistnienia jakiegoś faktu w rzeczywistym świecie. Aby to nieco rozjaśnić, to musimy sobie uzmysłowić, że fakt zarejestrowania danych w systemie mógł nastąpić później niż faktyczna zmiana tych danych w realnym świecie. Stąd podział na takie dwa przypadki.

Istnieje jeszcze trzeci sposób będący kombinacją dwóch poprzednich nazywany bitemporal.

Wprowadźmy oś czasu i rozpatrzmy przykład prostego obiektu Pracownik posiadającego dwa pola: ImieNazwisko, Departament.

2004-08-01: Id = 1, ImieNazwisko = „Janina Nowak”, Departament = „PR”

2005-11-15: Id = 1, ImieNazwisko = „Janina Nowak”, Departament = „HR”

2009-08-08: Id = 1, ImieNazwisko = „Janina Kowalska”, Departament  = „HR”

2013-01-01: Id = 1, ImieNazwisko = „Janina Kowalska”, Departament  = „IT”

Teraz naszego pracownika możemy rozpatrywać w kontekście czasu. Załóżmy, że powyższe daty odnoszą się do świata rzeczywistego, a nie momentu zarejestrowania tych faktów w systemie. Wiemy że nasz byt rozpoczął pracę w 2004 roku w departamencie PR i wiemy w jakim departamencie pracował w maju 2008. Dodatkowo wiemy, że nasz pracownik, a raczej pracowniczka w międzyczasie zmieniła nazwisko.

Wprowadziliśmy dodatkowy atrybut ValidTime określający czas od jakiego obowiązuje konkretna wersja danych tego samego pracownika.

Zastanówmy się zatem jak przechowywać tego typu dane w relacyjnej bazie danych. Sposobów jest kilka, ale niewątpliwie korzystne wydaje się być wprowadzenie okresu czasu. Dzięki okresowi czasu tj. atrybutom ValidTimeFrom i ValidTimeTo możemy wydajniej i prościej wyszukiwać dane w naszym systemie. Ciężar wtedy przenosi się na zapis danych. I tak np. nasz pracownik będzie wyglądał teraz tak:

ValidTimeFrom = 2004-08-01, ValidTimeTo =   2005-11-14, Id = 1, ImieNazwisko = „Janina Nowak”, Departament = „PR”

ValidTimeFrom = 2005-11-15, ValidTimeTo =  2009-08-07, Id = 1, ImieNazwisko = „Janina Nowak”, Departament = „HR”

ValidTimeFrom = 2009-08-08, ValidTimeTo =  2012-12-31, Id = 1, ImieNazwisko = „Janina Kowalska”, Departament = „HR”

ValidTimeFrom = 2013-01-01, ValidTimeTo =  NULL, Id = 1, ImieNazwisko = „Janina Kowalska”, Departament = „IT”

Kluczem identyfikującym nasz obiekt (jego konkretną wersję w czasie) jest jego Id i ValidTimeFrom. Pole ValidTimeTo jest tylko polem pomocniczym ułatwiającym dostęp do danych.

Łatwo możemy pobrać dane obowiązujące w konkretnym punkcie czasu:

SELECT ImieNazwisko, Departament FROM Pracownicy WHERE Id = 1 AND ‚2008-05-01’ BETWEEN ValidTimeFrom AND ValidTimeTo

Jak widać zostało tu poczynione założenie, że wszystkie dane z całego zakresu czasu znajdują się w jednej tabeli. Innym rozwiązaniem jest przechowywanie danych bieżących w jednej tabeli, a danych archiwalnych w drugiej np. Pracownicy_Archiwum. Ten drugi wariant jednakże ma sens w sytuacji używania TransactionTime w naszym systemie. Mianowicie, gdy nasz system ma obowiązek rejestrowania historii zmian poczynionych na danych, ale sięga do nich sporadycznie – wybierzmy wariant nr 2.

A co z gotowymi rozwiązaniami?

Co prawda pojęcie temporal tables pojawiło się w standardzie SQL:2011, ale obecnie jeszcze niewiele baz danych obsługuje tą funkcjonalność. Jeśli używacie MS SQL Server – zapomnijcie! Jedną z ciekawszych implementacji, moim zdaniem, proponuje IBM w DB2 od wersji 10.

A co z modelem domeny?

Świat zareagował oczywiście na potrzeby odwzorowania danych temporal także w modelu obiektowym. Powstało kilka wzorców projektowych odnoszących się mniej lub bardziej do tego zagadnienia. Sam Martin Fowler zaangażował się w ten temat i omawia kilka z nich tutaj.

Słowem podsumowania dodam, że póki co programiści związani z technologiami Microsoft (.NET, SQL Server) muszą pokusić się o własne implementacje.

Moim celem nie było dostarczenie konkretnego i uniwersalnego rozwiązania, bo takie pewnie nie istnieje, a raczej nakreślenie problemu i zachęcenie do zgłębienia wiedzy na ten temat.

3 kwietnia 2012

Chcesz być Git?

Filed under: Codzienne dylematy modelarza — Tagi: , — Beniamin Zaborski @ 20:06

W tym poście chciałbym odnieść się do jakże popularnego ostatnio tematu rozproszonych systemów kontroli wersji. Jedne z najczęściej dziś wymienianych to oczywiście tytułowy Git, ale także Mercurial czy Bazaar. Na wstępie zaznaczę że to nie będzie typowy tutorial o Git-cie, a raczej moje luźne przemyślenia na jego temat.

Jako developer wychowany na scentralizowanych systemach kontroli wersji głównie SourceSafe czy SVN, nauczyłem się że nie należy od nich wymagać wiele (szczególnie od tego pierwszego). Stawiając poprzeczkę odpowiednio nisko wszystko było w porządku. Checkout działa, checkin działa – pełna radość!

Pomimo ogólnie panującego przekonania, że SourceSafe nie jest git udawało się jakoś z nim współpracować nawet z obszernymi projektami w kilkunastoosobowych zespołach. Wszystko się zmienia po dokładniejszym poznaniu Git-a. Jego możliwości wręcz oszałamiają, a do tego wszystkiego to po prostu działa.

Po pierwsze należy przyswoić to, że Git to system rozproszony. Co to właściwie oznacza? Mianowicie tyle, że nie ma jednego centralnego repozytorium, a jest ich wiele, dowolnie wiele… W praktyce oczywiście pewnie jedno z repozytoriów będzie pełnić rolę takiego „centralnego”, ale nikt na nim nie będzie bezpośrednio pracował. Być może będzie z niego pobierał źródła jakiś system continous integration aby wykonać builda wersji.

Idea „rozproszoności” opiera się na tym, że każdy z developerów przed przystąpieniem pracy tworzy sobie lokalnie kopię repozytorium. Uwaga jest to kopia zawierająca pełną historię. Ta operacja tworzenia lokalnej kopii według nomenklatury Git nazywana jest klonowaniem (clone). Następnie praca odbywa się na lokalnym repozytorium, łącznie z commitami które są robione również lokalnie. Przeniesienie zmian z repozytoriów lokalnych do „centralnego” wykonujemy za pomocą push. Typowy cykl pracy mógłby wyglądać tak:

1) Utworzenie repozytorium „centralnego”

git init

2) Utworzenie kopii lokalnych repozytoriów

git clone

3) Praca z plikami w repozytoriach lokalnych

git add .

4) Zatwierdzanie zmian w repozytoriach lokalnych

git commit

5) Przeniesienie zmian do repozytorium „centralnego”

git push

6) goto 3

Jednym z założeń autora Git-a tj. Linusa Torvaldsa było to aby ten system był szybki. Rzeczywiście jest. Co prawda wersja Windowsowa, która działa na emulowanym środowisku POSIX Cygwin jest nieco wolniejsza od tej *nix-owej, ale nie można jej niczego zarzucić pod kątem wydajnościowym.

Git-a klasycznie obsługujemy z linii komend. Wiem, że teraz wielu z Was powie, że to jakieś nieporozumienie. Moja pierwsza reakcja była podobna – ech co się ze mną dawnym *nix-owcem stało:). Uwierzcie mi, że obsługa Git-a nawet za pomocą linii komend jest naprawdę przyjemna! Dosyć intensywnie rozwijają się narzędzia do współpracy z Git-em. Już dziś mamy dobrze rozwinięty i stabilny TortoiseGit, który integruje się z powłoką Windows. Poza tym są dodatki do Visual Studio pozwalające cieszyć się Git-em w tym środowisku. Tu należy zwrócić uwagę na GitExtensions oraz Git Source Control Provider. Oba te dodatki może nie powalają swoim wyglądem, ale nadrabiają na pewno funkcjonalnością.

Nie wspomniałem jeszcze o wyśmienicie działającym tworzeniu rozgałęzień oraz ich łączeniu. To ogromna zaleta Git-a. Teraz już nie trzeba się tego bać. Można śmiało:

1) Utworzyć nową gałąź

git branch nazwa_gałęzi

2) Pracować z plikami

git add .

3) Zatwierdzać zmiany

git commit

4) Przełączać się między rozgałęzieniami

git branch master

5) Łączyć rozgałęzienia

git merge nazwa_gałęzi

 

Repozytorium „centralne” może znajdować się gdzieś w systemie plików na zdalnie udostępnionym zasobie, albo można do tych celów zainstalować dedykowany serwer. Jest ich kilka, obsługujących własny protokół git. Patrząc z punktu widzenia developera w systemach Windows ciekawe rozwiązanie jako serwer Git to Bonobo Git Server. To aplikacja ASP.NET MVC 3 hostowana na IIS. Instalacja jest szybka i bezproblemowa a do tego wygodny i prosty interfejs Web do zarządzania repozytoriami, użytkownikami, itp.

Polecam! Bądź Git!

23 czerwca 2010

Prośby zostały wysłuchane, czyli POCO w Entity Framework.

Filed under: Codzienne dylematy modelarza — Tagi: , , — Beniamin Zaborski @ 23:13

Jakiś czas temu pisałem na blogu czemu nie lubię Entity Framework, narzekałem i wytykałem błędy (zresztą nie byłem w tym sam). W tym momencie muszę przyznać, że Microsoft stanął na wysokości zadania i „naprawił” to co mnie najbardziej irytowało w Entity Framework. Tak – teraz encje są obiektami POCO, a sam Entity Framework ma cechy O/R Mappera w stylu persistence ignorant. Wszystko jest w najlepszym porządku. Dla tych, którzy nie widzą problemu w używaniu O/R Mappera, który nie jest persistence ignorant mam dobrą wiadomość, bo nadal EF może działać po „staremu” i domyślnie tak działa.

Aby jednak móc skorzystać z tego cudu jakim jest persistence ignorant w EF, w Visual Studio należy doinstalować odpowiedni generator. Ta operacja w Visual Studio 2010 jest dziecinnie prosta, gdyż wystarczy po utworzeniu modelu EF z menu kontekstowego designera encji wybrać „Add Code Generation Item…” -> Online Templates -> Templates -> Database -> „ADO.NET C# POCO Entity Generator”. Po zainstalowaniu dodatku zobaczymy w naszym solution pliki szablonów *.tt oraz związany z nimi custom tool TextTemplatingFileGenerator.

Dzięki szablonom i generatorowi możemy się cieszyć encjami POCO w EF. Obiekty encji teraz to czyste obiekty, które nie dziedziczą już ze specyficznej klasy bazowej jak to w „starym” EF było. Dodać należy jeszcze tylko tyle, że działa tu lazy loading oraz change tracking.

Jest dobrze, ale czy nie może być lepiej? Zawsze może! W związku z tym zawsze można wprowadzić modyfikacje do szablonów generatora, aby przystosować generowany kod jeszcze bardziej do własnych potrzeb. Zachęcam do zapoznania się z POCO Entity Generator dla Entity Framework. Hmm … może to właśnie EF kiedyś zastąpi NH w moich projektach (?).

20 marca 2010

Dlaczego NHibernate jest wciąż moim ulubionym O/R Mapperem?

Filed under: Codzienne dylematy modelarza — Tagi: , — Beniamin Zaborski @ 11:24

W zasadzie pytanie powinno brzmieć: dlaczego Entity Framework szybko nie stanie się moim ulubionym O/R Mapperem? Po kilkuletniej przygodzie z NHibernate postanowiłem, jakiś czas temu, sprawdzić jak się ma konkurencja. Po szumnych zapowiedziach Microsoftu o inwestowaniu ogromnych sił i środków w rozwój ich flagowego, jak się okazuje, O/R Mappera sięgnąłem po Entity Framework. Coś musi być na rzeczy bo w „branży” dwie litery EF stały się mocno popularne – wręcz trendy. Zachęcony tym całym szumem wokół tej technologii, a także mając w pamięci prelekcję Julii Lerman z C2C’09 stworzyłem swój pierwszy projekt w EF. Hmm … ładny designer encji;). Tu się wszystko skończyło – dawno nie byłem tak rozczarowany! Dwie kwestie rozłożyły wszystko:

  • obiekty domeny nie są POCO
  • O/R mapper nie jest „Persistence Ignorance”

Obie te dwie cechy mają ze sobą wiele wspólnego. Przede wszystkim mój kod z użyciem EF posiada zbyt wiele zależności do konkretnych technologii łamiąc zasady architektury aplikacji biznesowych jak i wielu wzorców projektowych. Oczywistym mogło by się wydawać to, iż O/R Mapper nie powinien wymagać dziedziczenia encji po własnych klasach. Tak samo oczywiste jest to, że domena aplikacji nie powinna mieć świadomości o mechanizmie zapisu/pobierania danych. A jednak! Bez sarkazmu i zupełnie szczerze mówiąc, nie rozumiem tak wielkiej ignorancji twórców EF. Przyglądam się i nadal kibicuję EF mając nadzieję, że kolejne wersje przyniosą znaczące zmiany w tych dwóch kwestiach. Tymczasem zostaję przy sprawdzonym NHibernate.

13 Maj 2009

DO or not DO?

Filed under: Codzienne dylematy modelarza — Tagi: , , , , — Beniamin Zaborski @ 07:06

DO or not DO? DO jak Data Object, zwane także DTO – Data Transfer Object, VO – Value Object czy nawet Presentation Entity. Używać czy nie używać?
Oczywiście nie spodziewajcie się jednoznacznej odpowiedzi, a jeśli już taka padnie to pewnie będzie dość subiektywna. Problem ten przewija się na wielu forach i stosowanie obiektów DTO ma tyle samo przeciwników co zwolenników. Dużo zależy jednak od aplikacji jaką piszemy, od jej typu, rozmiaru, itp. Ja jednak obecnie zaliczam się do grupy zwolenników stosowania obiektów DTO (choć może mnie ktoś przekona i zmienię zdanie). Czemu? To postaram się właśnie tutaj przedstawić. Zanim jednak zacznę należy się krótkie wprowadzenie teoretyczne.
Czasy gdzie dominującym modelem architektonicznym w aplikacjach biznesowych był klient-serwer bezpowrotnie (mam nadzieję) minęły. Dziś przystępując do projektu z góry zakładamy model n-warstwowy (szczególnie 3-warstwowy). Oznacza to, że w naszej aplikacji będzie można wyróżnić co najmniej następujące warstwy logiczne:
– warstwa dostępu do danych
– warstwa logiki biznesowej
– warstwa prezentacji.
Jest to model bardzo ogólny i odnosi się do wszystkich typów aplikacji czy to web-owych czy desktopowych. W kontekście poruszanego tematu nas najbardziej będzie interesować styk warstwy logiki biznesowej i warstwy prezentacji.
Nasuwa się pytanie: czy do warstwy prezentacji przesyłać bezpośrednio encje biznesowe? Ja jednak postawię to pytanie inaczej: dlaczego nie przesyłać do warstwy prezentacji encji biznesowych?
Jak już wspomniałem wcześniej jestem zwolennikiem stosowania obiektów DTO. Zdaję sobie sprawę, że wymaga to dodatkowej pracy (co często wytykają przeciwnicy), ale to procentuje później.
Warto zerknąć tutaj i zobaczyć jak Martin Fowler przedstawia Data Transfer Object.

Oto co przemawia za tym rozwiązaniem:

1) Luźnie powiązania.

Podstawową zaletą takiego rozwiązania jest wprowadzenie kolejnego luźniejszego powiązania pomiędzy warstwą logiki biznesowej, a prezentacji. Ma to szczególne znaczenie, kiedy nagle okaże się, iż trzeba dokonać pewnych zmian w warstwie prezentacji. W ten sposób często unikamy modyfikacji w warstwie logiki biznesowej, co wydaje się być dobre.

Przeciwnicy powiedzą: W większości typowych przypadków i tak dokonanie zmian w warstwie logiki biznesowej będzie niezbędne.

2) Różne wymagania.

Encja reprezentująca jakiś fragment naszego biznesu z jednej strony musi komunikować się z klientem (warstwa prezentacji), a z drugiej z repozytorium w którym jest przechowywana (warstwa dostępu do danych). Oba te wymagania nakładają na encje pewne zadania. Patrząc od strony warstwy dostępu do danych to powinno być możliwe wykonanie operacji CRUD na encji. Czy to poprzez procedury składowane, czy mechanizm O/R Mapping-u. Z drugiej strony encja powinna współpracować z warstwą prezentacji, tj. bindować się z kontrolkami, obsługiwać mechanizmy powiadamiania o niespełnionych regułach walidacyjnych itp. To znowu nakłada na każdą encje zupełnie inne zadania. Stąd warto rozdzielić te dwa jakże odmiene od siebie grupy zadań, powierzając komunikację z warstwą prezentacji właśnie specjalnie do tego wydzielonym obiektom DTO.

Przeciwnicy powiedzą: Zbyt duży nakład pracy, a korzyści nie tak widoczne jak by się można było tego spodziewać.

3) Większa wydajność.

W czasach systemów rozproszonych często warstwy aplikacji znajdują się na różnych maszynach stąd komunikują się poprzez sieć. Komunikacja ta może się odbywać z wykorzystaniem różnych mechanizmów jak WebService (SOAP), .NET Remoting, COM+, itp. Często do pojedynczych widoków w warstwie prezentacji przesyłamy tylko część danych z encji. Nie zawsze encje pokrywają się 1 do 1 z widokami. Zamiast przesyłać poprzez sieć duże obiekty encji biznesowych warto przepakować do obiektów DTO tylko te dane, które faktycznie będą nam potrzebne w widoku.

Przeciwnicy powiedzą: W dzisiejszych czasach wydajność systemów to nie problem, zresztą kto na to zwraca uwagę.

Jak napisałem na początku odpowiedzi jednoznacznej nie ma. Bez dwóch zdań koszty pracy związane ze stosowaniem obiektów DTO są wyższe, no  przynajmniej na początku. Warto rozważyć, przed przystąpieniem do nowego projektu, stosowanie obiektów DTO, co nie oznacza, że w każdym przypadku będzie to trafione rozwiązanie. A więc pytanie „DO or not DO” pozostaje otwarte …

Stwórz darmową stronę albo bloga na WordPress.com.