piątek, 19 maja 2017

Nowa praca, nowe początki

Witam ponownie po dłuższej przerwie!
Piszę do was będąc na pierwszej delegacji we Francji i mając już ponad dwa tygodnie w nowej firmie za sobą. Siedzę w hotelu w małym miasteczku - Obernai gdzie mieści się zarazem główna siedziba mojej nowej firmy, którą jest (fanfary):


Firma produkcyjna - o korporacyjnym charakterze, rodzinnym kapitale i posiadająca kilka interesujących marek. Dla mnie najbardziej nieznaną sprawą była (i wciąż jest) korporacyjność. Przeskok organizacyjny pomiędzy małą polska firmą, a międzynarodową korporacją jest ogromny i ciężko mi jeszcze określić co dokładnie jest plusem, a co minusem - na pewno plusem jest jasno określona hierarchia, zakres obowiązków i pełny wgląd do wszelkich regulaminów, systemu benefitów itp. Największym minusem jest chyba polityka bezpieczeństwa IT - na komputerach nie mamy dostępu do konta admina, a cały ruch sieciowy leci przez proxy we Francji i bez aktywnego VPNa nie można się połączyć z internetem poza firmą. Pewnie wrócę do tematu plusów i minusów jeszcze w przyszłości aczkolwiek jestem jak na razie zadowolony.

Moja praca polega na pisaniu aplikacji mobilnej o nazwie 123Schema z wykorzystaniem Xamarin.Native przy użyciu frameworka MVVMCross, o którym jeszcze później wspomnę. Pracuję w 4-ro osobowym zespole składającym się ze mnie, kolegi juniora w PL, architekta/team leadera oraz z seniora we Francji. Do zarządzania cyklem życia aplikacji używamy TFSa, w którym również siedzi kod z wykorzystaniem GIT-a. Jest to dla mnie nowość i po tych dwóch tygodniach jestem BARDZO zadowolony z używania takiego rozwiązania. Backlog, tablica Kanbana, daily meetingi, zarządzanie taskami, branchami, pull requestami - wszystko na prawdę współgra i tworzy spójną całość, w której natychmiast się odnalazłem i czekam jeszcze tylko na integrację z SonarQubem i rozpoczęcie korzystania z Continous Integration. Muszę również powiedzieć szczerze, że architekt jest OK - bardzo rygorystycznie przestrzega wszelkich standardów i bardzo szczegółowo pisze wymagania. Ma sporą wiedzę o wzorcach projektowych i dobrych praktykach  - chociaż  raz chciał zastosować anty-pattern IRepository<T> -  ale jak zawsze mnie wysłuchał i ogólnie jest otwarty na pomysły i dyskusje. 

MVVMCross powoli poznaje i odkrywając jego funkcjonalność dodawaną w kolejnych betach przekonuje się że to dobre narzędzie ALE .... mimo wszystko cały czas obstawiam, że Xamarin.Forms byłby lepszy :) Niestety decyzje o wyborze stosu technologicznego zapadły na długo zanim projekt się zaczął i ja pojawiłem się w firmie więc biorę co jest i traktuje to jako zawodowe wyzwanie - zawsze to przyjemnie nauczyć się czegoś nowego.



Czekają mnie jeszcze co najmniej dwie dwutygodniowe podróże tutaj, by na miejscu sprawnie się konsultować, integrować i poznawać ludzi z IT - z jednej strony bardzo fajnie, a z drugiej niestety takie wyjazdy są bardzo uciążliwe ze względów rodzinnych i m.in pszczółkowych - przez 3 miesiące okresu próbnego - połowę czasu spędzę we Francji. 

Na zakończenie mogę powiedzieć, że ogólnie sprawy zaczynają się stabilizować - mam nadzieję w najbliższych tygodniach wrócić do DSP i kontynuować pisanie mojej konkursowej aplikacji. Jednocześnie pozdrawiam kolegów z inSolutions i życzę wszystkiego dobrego! Do kolejnego przeczytania :)


niedziela, 23 kwietnia 2017

Update na blogu

Witam wszystkich po przerwie.
Niestety jak widać od pewnego czasu na blogu jest trochę pusto i cicho - postaram się pokrótce wytłumaczyć dlaczego do tego doszło i co dalej z blogiem.
Powód pierwszy, najważniejszy - zmieniam pracę! Tak - 6 lat i 2 miesiące to mój staż w obecnej firmie i czas ten dobiegł końca. Już za tydzień trafię do nowego zespołu i zaczynam nowy życiowy rozdział - razem z naszym przyjacielem - Xamarinem. No i teraz do sedna - dlaczego nie piszę postów? Otóż projekty w nowej firmie będę robił w podejściu natywnym i cały swój wolny czas staram się poświęcić na podszkoleniu z platformowych API. Trochę to nie pasuje do Xamarin.Forms w ramach którego piszę swój projekt na DSP więc chwilowo muszę projekt wstrzymać, aczkolwiek dobitnie podkreślam że go nie anuluje!
Gorący i stresujący okres przez który przechodzę , zwalnianie się, wykańczanie projektów, które zostawiam i niepewność co zastane w nowej pracy to zły czas na prowadzenie bloga - dodatkowo przez pierwsze trzy miesiące czekają mnie służbowe wyloty do francji na wdrożeniowe szkolenia.
 Zachęcam jednak do zaglądania co jakiś czas bo na pewno będę relacjonował początki w nowej pracy, a jak tylko sytuacja się ustabilizuje to wracam do "Mojej Pasieki".
No ale jeszcze jest powód drugi - powód dla którego projekt powstał i powód, który również zajmuje sporo mojej uwagi. Pszczółki, bo o nich mowa maja się świetnie - przezimowały wspaniale, a ich wiosenny rozwój troche mnie przerasta. Przy okazji ostatniej lepszej pogody chciałem im pouzupełniać brakujące ramki - dwie rodziny zimowały na dwóch korpusach wielkopolskich z ramkami ułożonymi w komin po 7-8 ramek na korpus, jedna na 1,5 korpusu, a ostatni odkład zimował na 9 ramkach w jednym korpusie. Widok jaki zastałem po dwóch tygodniach:


Jak widać ramki z węzą chciałem im dać zbyt późno - same sobie pouzupełniały wg uznania, a część już pozalewały świeżym nakropem - najprawdopodobniej z klonów, mleczy i drzew owocowych. Praca na pasiece wre, sezon oficjalnie rozpoczęty - w garażu tworzą się korpusy, dennice i daszki, a w domu unosi się zapach wosku z wtapianej w nowe ramki węzy. 
Wybaczcie mi więc tą ciszę - życie wymusza skupienie uwagi na innych sprawach. Obiecuję o was nie zapomnieć i wracać z nowymi postami jak tylko często się da.

niedziela, 9 kwietnia 2017

Jak dodać projekt platformy do już istniejącej solucji.

Cześć,
dzisiaj małe tips&tricks. Na początek przepraszam za brak postów ostatnio na blogu - powody opiszę w najbliższej przyszłości - zapewniam, że są racjonalne :)

O co chodzi w temacie posta? Problemem jest dodanie nowego projektu dla konkretnej platformy w juz istniejącej solucji Xamarin.Forms. Niestety żadne IDE Xamarinowe nie posiada takiej funkcjonalności, zarówno Xamarin Studio na OS-x i Windowsie ani Visual Studio.

Xamarin Studio na OSX

Rozpoczynając projekt mojej konkursowej aplikacji popełniłem błąd i zacząłem pisać ją w Xamarin Studio na Windowsie - tam niestety platforma iOS nie jest wspierana. Miałem więc solucję z projektami Forms, Droid i Test - co zrobić, żeby dodać tam projekt na iOSa?

Rozwiązanie jest proste choć nieoczywiste. Trzeba utworzyć nową solucję o takiej samej nazwie jak obecna (ten sam package name) i zaznaczyć brakującą platformę. Fizycznie kopiujemy projekt iOS do naszej pierwotnej solucji, a następnie już w IDE dodajemy istniejący projekt, wyklikujemy dodanie referencji i gotowe.
Mnie udała się ta sztuka za pierwszym razem, a do nadrobienia miałem dodanie inicjalizacji paru nugetowych pakietów w AppDelegate oaz implementację dwóch interfejsów z DependcencyService.
Analogicznie powinno to działać w przypadku braku projektu Androidowego.

Teraz moja solucja wygląda następująco:

widok na OSX

Widok na Windowsie

niedziela, 26 marca 2017

Jak narysować coś w Xamarin.Forms

Hej,
dzisiaj kolejny problem jaki musiałem rozwiązać w formsach. Chciałem narysować ul. W zależności od jego typu, elementów i danych wprowadzonych przez użytkownika rysunek miał się różnić tak aby oddawać jego rzeczywisty stan więc wstawienie gotowego rastra odpadało. Schemat ula musi być więc wygenerowany w kodzie - niestety ograniczenia formsów znowu okazały się zbyt duże. Tak na prawdę musimy poprzestać na samym prostokącie, który możemy zrealizować np poprzez kontrolkę BoxView lub każdą inną, która ma właściwość BackgroundColor. W Adobe AIR sprawa byłaby prosta - większość kontrolek wystawia nam obiekt klasy Graphics, który pozwala na rysowanie wektorowe przeróżnych kształtów. Po, krótkim researchu odnalazłem jej odpowiednik dla Xamarina - po pierwsze NGraphics, które pozwala na obsługe wektorowej grafiki na każdej z platform korzystając z natywnych konrolek, a po drugie - NControl, która to biblioteka opakowuje pierwszą w kontrolkę, dziedziczącą po Xamarin.Forms.View. Sprawa jest o tyle przyjemna, że mając naszą biblioteke rysującą w postaci View - możemy nie naruszając architektury MVVM rysować i bindować do widoku nasz rysunek np poprzez:
<ContentView Content="{Binding BeeHiveDrawing}" />
całkowicie w naszym modelu widoku:
public Xamarin.Forms.View BeeHiveDrawing
{
    get
    {
        return _beeHiveDrawing;
    }
    set
    {
        _beeHiveDrawing = value;
        OnPropertyChanged(nameof(BeeHiveDrawing));
    }
}
Ok, przejdźmy więc dalej. Sama inicjalizacja naszej kontrolki jest standardowa  i opisana pod linkiem podanym powyżej. Jej przykładowe użycie wygląda następująco:
var nc = new NControlView((NGraphics.ICanvas canvas, NGraphics.Rect rect) =>
{
    canvas.DrawPath(new PathOp[] 
    { 
        new MoveTo(0, 0),
        new LineTo(rect.Width, rect.Height) 
    }, 
    new Pen("#ff0000", 3));
});

W konstruktorze NControlView podajemy Action<ICanvas, Rect> gdzie otrzymujemy nasz element - płachtę, po której rysujemy oraz jej finalne wymiary w postaci obiektu NGraphics.Rect. Mamy więc od czego zacząć - w następnym poście zaprezentuje jak poradziłem sobie z zadaniem, opisanym na początku - rysowaniem ula.

środa, 22 marca 2017

Mapy i geolokalizacja w Xamarin.Forms

Hej,
w moim projekcie, zaraz na początkowym etapie miałem potrzebę użycia map. Dokładnie potrzebowałem poprosić użytkownika o wskazanie lokalizacji pasieki - w przyszłości mam zamiar wykorzystać te dane do pokazywania użytkownikowi z jednego z dostępnych API  pogody na pasiece i w pewnych przypadkach pokazywać mu notyfikacje (moja główna wartość biznesowa). Na start trafiłem na przygotowany przez ekipę Xamarina bibliotekę Xamarin.Forms.Maps, której użycie i konfiguracja opisana jest tutaj.
Niestety szybko okazało się, że funkcjonalność oferowana przez ten pakiet jest uboga i ogranicza się głównie do kontrolki mapy, na której możemy pokazać nieinteraktywnego pina. Szybko trafiłem na bibliotekę, która bazując na w/w bardzo rozszerza jej funkcjonalność, a którą bardzo polecam: TK.CustomMap - z jej pomocą możemy dodawać customowe, przeciągalne piny, rysować na mapie kształty, wyznaczać trasy itp. Instalacja z nugeta jest bezproblemowa i po konfiguracji opisanej np na githubie możemy zacząć dodawać mapy.

Na potrzeby testów i ewentualnej zmiany używanej biblioteki dodałem sobie interfejs:
public interface IMap
{
    void showMap();
    Task showMapForLocation(Action<string> onUserSelectPoint, double currentLat = 0, double currentLng = 0);
}

Pierwsza metoda służy mi do pokazania mapy - zapewne będę ją rozbudowywał o dodatkową funkcjonalność, jednak do zabawy z mapami po prostu pokazuje okienko z mapą. W samej aplikacji korzystam na razie tylko z drugiej funkcji i na jej przykładzie omówię moje użycie biblioteki - pełna implementacja jest wrzucona na githubie.
Na początek tworzę obiekt mojej mapy z początkową lokalizacją gdzieś w Polsce (52,20):
var map = new TK.CustomMap.TKCustomMap(MapSpan.FromCenterAndRadius(new Position(52, 20), Distance.FromKilometers(100)));

Utworzony obiekt dodaję do wcześniej przygotowanego widoku, a następnie tworzę obiekt pina,  dodaje go do mapy, a po kliknięciu w mapę, przesuwam pina w te miejsce.
var pin = new TK.CustomMap.TKCustomMapPin { Position = map.MapCenter, IsDraggable = true, ShowCallout = true, Title = "Lokalizacja pasieki" };
map.CustomPins = new List<TK.CustomMap.TKCustomMapPin> { pin };
map.MapClicked += (object sender, TK.CustomMap.TKGenericEventArgs<Position> e) =>
{
    pin.Position = e.Value;
};

W ten sposób, użytkownik może zarówno klikając w mapę jak i przeciągając pina po niej łatwo wskazać lokalizację. Teraz pozostaje nam temat drugi - czyli geolokalizacja. Na początek w celu ułatwienia użytkownikowi odnalezienia swojej pasieki staram się wycentrować mapę na jego bieżacej lokalizacji - użyłem do tego biblioteki Xam.Plugin.Geolocator - polecam
ją bo korzysta z async/await oraz sama pyta o przyznanie uprawnień w Androidzie 5.0+. Jej użycie sprowadza się tak naprawdę do paru linijek kodu:

if (Plugin.Geolocator.CrossGeolocator.Current.IsGeolocationAvailable && Plugin.Geolocator.CrossGeolocator.Current.IsGeolocationEnabled)
{
    userLocation = await Plugin.Geolocator.CrossGeolocator.Current.GetPositionAsync(10000);
    var userPosition = new Position(userLocation.Latitude, userLocation.Longitude);
    map.MoveToRegion(MapSpan.FromCenterAndRadius(userPosition, Distance.FromKilometers(3)));
    pin.Position = userPosition;
}

Jeżeli api geolokalizacji jest dostępne - pobieramy lokalizację z timeoutem 10s, zmieniamy strukturę lat/lng na tą z TK.CustomMaps i centrujemy mapę i pina. Na początek na moje potrzeby taka funkcjonalność jest wystarczająca chociaż będę musiał dodać jeszcze pole tekstowe do wyszukiwania adresu.  Dodanie obsługi map w aplikacji - korzystając z tutoriali i opisów z zamieszczonych tu linków nie nastręczyło mi większych trudności, a mam nadzieję że korzyść z tego płynąca będzie spora. Pozdrawiam i do następnego razu!


niedziela, 19 marca 2017

XAMARIN i SQlite

Hej,
dzisiaj trochę o bazie danych w naszej konkursowej aplikacji - cóż to by była za aplikacja bez bazy, prawda? Przeglądając wszystkie xamarinowe tutoriale wybór wydaje się oczywisty: SQLite. Z takiego też rozwiązania korzystam, aczkolwiek gwoli ścisłości sposobów na przechowywania danych w xamarinie jest sporo - możemy równie dobrze dane parsować do jsona i korzystając z PCL Storage trzymać je w plikach. Ale ponieważ SQLite jest natywnie wspierany pod wszystkimi mobilnymi platformami, oraz korzystałem z niego wcześniej pisząc pod Adobe AIR nie zastanawiałem się długo nad wyborem. W internecie jest więcej niż jedna biblioteka tego bazodanowego silnika, ale najbardziej wypromowana i polecana jest sqlite-net-pcl wywodząca się z .netowej implementacji o nazwie sqlite.net.

Sam proces tworzenia naszej bazy jest dobrze opisany w internecie np tu : link.
Korzystanie jest proste i przyjemne jednak mamy pewne ograniczenia wynikające ze stosowania ORMa oraz z trochę okrojonego silnika. Po tych kilku latach używania SQLite nazbierało mi się kilka uwag i upierdliwych problemów:

  • Pod każdy interesujący nas wynik zapytania musimy zrobić osobną encje - nie można po prostu zrobić joina dwóch tabelek i dostać wyniku w postaci List<object>. Bardzo brakuje mi możliwości zwrócenia wyniku w postaci Hashtable, ExpandoObject lub w inny podobny sposób, który nie angażowałby mnie tak bardzo w pisanie encji. Korzystając z bazy w Adobe Air, w taki właśnie sposób działało zwracanie wyników - dostawaliśmy tablicę AS3.0 obiektów które zachowują się tak jak ExpandoObject w C#. 
  • Brak kluczy zewnętrznych - aczkolwiek znalazłem bibliotekę, która taką funkcjonalność oferuje (link) ale pewnie daje to spory narzut bo sam silnik takiej funkcjonalności nie posiada. 
  • ALTER TABLE obsługuje tylko ADD COLUMN - żadnej zmiany na istniejącej kolumnie nie wykonamy - nawet jej nie usuniemy. Trzeba się bawić w przenoszenie danych do tabelki tymczasowej, drop, nowy create itd.
  • Plik bazy ma tendencje do puchnięcia - polecam co jakiś czas wykonać VACUUM.
  • Error: database disk image is malformed - kto nigdy tego błędu nie widział ten może się uznać za szczęśliwca. Niestety mając kilka tysięcy urządzeń na produkcji raz na jakiś czas zdarza się, że w środowisku wystąpi błąd który plik bazy nieodwracalnie uszkadza (link). Dostęp do którejś z tabel jest wtedy niemożliwy, a resztę zwykle staram się odzyskać i w locie przerzucić do nowo utworzonej bazy. Bardzo irytujący błąd, którego nie potrafię wyeliminować.


Podsumowując - wybór silnika bazy danych był dla mnie prosty. Nie zawiodłem się na nim i polecam SQLite każdemu, kto potrzebuje skorzystać z relacyjnej bazy danych na urządzeniu mobilnym.



niedziela, 12 marca 2017

Widok, Model Widoku - proste połączenie

Hej,
w tym poście chciałbym zaprezentować prosty i skuteczny sposób na kojarzenie dwóch tytułowych elementów w jeden. Słowem wstępu - najpierw kilka informacji o działaniu widoków w Xamarin.Forms:
Widoki możemy tworzyć na dwa sposoby: używając XAMLa oraz całkowicie w kodzie c#. Osoby korzystające z Xamarina lub WPFa pewnie z XAMLem miały już do czynienia, a jak nie to krótko ujmując jest to język oparty na XMLu służący właśnie do tworzenia warstwy interfejsu użytkownika. Ma on tą przewagę nad tworzeniem widoków w kodzie, że po krótkiej praktyce, jestem w stanie z wizualizować sobie interfejs w głowie - przydatna umiejętność.
Pisząc XAMLowy widok w Xamarin.Forms jest on podzielony na dwa elementy - plik *.xaml, w którym jest xml z widokiem i plik tzw "codebehing" - *.xaml.cs który jest podpięty pod plik *.xaml i wygląda on mniej więcej tak:
public partial class WidokTest : ContentPage
{
    public WidokTest ()
    {
        InitializeComponent();
    }
}
Jak widać dla naszego testowego widoku mamy klasę, w której określamy bazowy widok po którym dziedziczymy - ContentPage lub ContentView najczęściej, a w konstruktorze wywołujemy inicjalizację komponentu.
Na pytanie skąd ten partial odpowiedź jest prosta - drugą częścią naszej klasy jest właśnie nasz plik xamlowy, który kompilator przerabia na jedną, wspólną klasę.

Dobrą praktyką jest aby w codebehind robić jak najmniej, a wszystkie operacje na widoku działy się poprzez bindingi. No ale w naszej klasie testowej brakuje co najmniej jednego elementu: ustalenia właściwości BindingContext, który powinien być instancją jednego z naszych modeli widoków.

Stworzyłem więc taką klasę bazową:

public abstract class ViewPage<T> : ContentPage where T : IViewModel
{
    readonly T _viewModel;
    public T ViewModel
    {
        get { return _viewModel; }
    }

    public ViewPage()
    {
        using (var scope = IoC.container.BeginLifetimeScope())
        {
            _viewModel = scope.Resolve<T>(new TypedParameter(this.GetType(), this));
        }
        BindingContext = _viewModel;
    }

    public ViewPage(object viewModelContext)
    {
        using (var scope = IoC.container.BeginLifetimeScope())
        {
            _viewModel = scope.Resolve<T>(new TypedParameter(this.GetType(), this), new NamedParameter("context", viewModelContext));
        }
        BindingContext = _viewModel;
    }
}
Stosując taką generyczną klasę bazową dla widoków mogę łatwo i szybko wyciągnąć z kontenera odpowiedni model i podpiąć go pod widok. Korzystając z niej, nasza testowa klasa widoku wyglądałaby następująco:

public partial class WidokTest : ViewPage<WidokTestModel>
{
    public WidokTest ()
    {
        InitializeComponent();
    }
}

public partial class WidokTest : ViewPage<WidokTestModel>
{
    public WidokTest (object kontekstWidoku) : base(kontekstWidoku)
    {
        InitializeComponent();
    }
}

Jak widać ilość kodu w codebehind wzrosła bardzo nieznacznie, a mamy załatwiony najważniejszy element. Niestety wszystko ma swoją cenę - trzeba przypomnieć sobie o tym nieszczęsnym partialu - nasz klasa dziedziczy teraz po nowym elemencie  i musimy analogicznie zmienić to w XAMLu:

<test:ViewPage xmlns:test="clr-namespace:MyApp.Test" x:TypeArguments="test:WidokTestModel"

Wystarczy dodać namespace w którym jest nasza klasa bazowa, zmienić nazwę głównego elementu oraz dodać generyczny parametr. W ten sposób wszystko nam się ładnie kompiluje, a my w prosty sposób spinany ze sobą widoki i modele widoków.

czwartek, 9 marca 2017

CQRS część 4, ostania - Validator

Hej,
ostatnią integralną częścią mojego CQRSa są walidatory. Są to klasy które dostając komendę, sprawdzają jej poprawność i możliwość wykonania. Gdy dana komenda w aktualnym stanie systemu jest niemożliwa do wykonania np nie można usunąć ula, w którym jest jakaś pszczela rodzina lub dane komendy są niepoprawne, spoza zakresu itp - to wtedy zwracamy taką informację do szyny komend, a ona za pomocą wyjątku zwraca ją dalej - użycie zostało pokazane w implementacji szyny komend w poście numer 1 dotyczącym CQRSa.
Dla walidatorów tak jak i dla komend mam ścieżkę synchroniczną i asynchroniczną. Zacznijmy od interfejsów:

public interface IValidator<TCommand> where TCommand : ICommand 
{
    ValidationResult Validate(TCommand command);
}

public interface IValidatorAsync<TCommand> where TCommand : ICommandAsync
{
    Task<ValidationResult> Validate(TCommand command); 
}

Jedyną nową rzeczą w tym kawałku kodu to klasa ValidationResult, a przedstawia się ona następująco:

public class ValidationResult
{
    public bool Result { get; set; } = true;

    public List<string> Messages { get; private set; } = new List<string>();
}

Mamy tutaj wynik walidacji określający czy wykonać komende, czy też nie, oraz listę komunikatów o problemach - jest to lista, ponieważ w ten sposób nie muszę się martwić o prawidłową konkatenację moich błędów i mam też informację o ich ilości, a gdy chcę pokazać informację o błędach robię po prostu String.join("\n", validationResult.Messages). Ostatnim elementem moich walidatorów jest customowy wyjątek, który wygląda następująco:

public class ValidationException : Exception
{
    public ValidationResult Result { get; protected set; }

    public ValidationException(ValidationResult result)
    {
        this.Result = result;
    }
}
Te kilka klas i interfejsów zamyka mi temat sprawdzania poprawności komend - oczywiście za pomocą kontenera i automatycznej rejestracji z assembly wszystko działa bardzo wygodnie. Ten post kończy serię o CQRS, po kilku tygodniach używania tego wzorca jestem bardzo zadowolony, wprawdzie ilość klas wzrosła i odczuwam pewien większy narzut czasowy pisania logiki jednak jest to niska cena za efekt, który uzyskałem. Kod jest czytelny, prosty, łatwo testowalny i dużo trudniej jest złamać zasadę jednej odpowiedzialności. Będę korzystał z CQRSa na pewno częściej w swojej pracy zawodowej.

środa, 8 marca 2017

Pszczoły na wiosnę i przywitanie

Hej,
blog wprawdzie miał być programistyczny, ale skoro aplikacja jeszcze nie gotowa chciałbym publikować tutaj również trochę informacji o moich pszczółkach i różnych ciekawych pszczelarskich perypetiach. Przy okazji pogłębię też moją wiedzę domenową, na której opieram cały projekt aplikacji. Na początku słowem wstępu o samej pasiece.
Pszczołami interesuje się od lat ok. 4, a pszczoły pierwsze zakupiłem lat temu 3. Mam 4 ule wielkopolskie - klasyczne stojaki w tym 3 wykonane przeze mnie, a jeden zakupiony u bartnicy.pl. Pasieka jest zlokalizowana koło mojego domu, na przedmieściach Tychów - dzielnica Czułów. Każdy ul jest zasiedlony, w tym jeden zeszłoroczny odkład Elgon, jeden odkład Krainka i dwie średnio silne rodzinki: Krainka i jakiś mieszaniec Krainki z Buckfastem. Tak prezentowała się moja pasieka w roku 2016 po zrobieniu ostatniego odkładu, zwracam uwagę, że w ulu na pierwszym planie są dwie rodziny oddzielone sprytną dennico-powałką.


No i jest dzień 08.03.2017, zima powoli za nami, a sytuacja w pasiece wygląda tak:
- straty po zimie (na razie): 0%
- pierwszy oblot : 27.02.2017,
- pyłek noszą - matki czerwią,
- wkładki dennicowe włożone,
- krokusy zaczynają kwitnąć,
- dzisiaj pierwsza porcja fondantu z diamanta ok 0,5 kg na rodzinke podane - nie bardzo miałem jak sprawdzić ilość pokarmu więc asekuracyjnie dostały co nieco - mam nadzieje, ze nie spracuje tym zimowej pszczoły.

Zastrzegam, że to moja pierwsza zimowla i pierwszy rok pszczelarzenia całkowicie od początku do końca na swoim - wcześniej pszczoły miałem na spółke z teściem, a pasieka była zlokalizowana w beskidzie żywieckim. Mam nadzieję, że ta gałąź bloga będzie również uzupełniana o nowe treści w miarę postępowania sezonu - do następnego razu!

CQRS część 3 - Event, Consumer

Hej,
wracamy do naszego CQRSa. Omówiłem już jego dwa podstawowe elementy czyli Komendy i Zapytania i na tym teoretycznie sam wzorzec poprzestaje, jednak oglądając na githubie jego różne implementacje zdecydowałem dołożyć do niego te dwa przydatne elementy. Zdarzenia są oczywiście wywoływane tylko przez handlery komend bo tylko tam zmieniamy stan systemu. Przenosząc się z Flexa do Xamarina, naturalne były dla mnie Eventy - we Flexie są bardzo dobrze zaimplementowane, a ich propagacja w systemie, propagacja po drzewie renderingu w górę do korzenia, anulowanie itp były out of the box. Zmieniłem z tego powodu trochę konwencję nazewnictwa samych zdarzeń co zobaczycie za chwilkę w snippetach. Konsument - jest to interfejs, który implementują klasy nasłuchujące na zdarzenia - zazwyczaj modele widoków. Wstęp za nami, lecimy z kodem, marker interface dla zdarzeń:

public interface IEvent
{
}

I od razu nasz interfejs szyny zdarzeń, dla mojej wygody po przesiadce z Flexa zamiast IEventBus interfejs po którym odnajdujemy w kontenerze naszą szynę nazywa się tak:

public interface IEventPublisher
{
    Task PublishAsync<TEvent>(TEvent @event) where TEvent : IEvent;
    void RegisterAsyncConsumer<TEvent>(IConsumerAsync<TEvent> consumer) where TEvent : IEvent;
    void UnregisterAsyncConsumer<TEvent>(IConsumerAsync<TEvent> consumer) where TEvent : IEvent;
}

Musicie mi wybaczyć tą zmianę konwencji nazewnictwa, sama klasa implementująca nazywa się już poprawnie :) Jak widać z samego interfejsu - wszystkie eventy obsługiwane są asynchronicznie - z początku chciałem zrobić dwie ścieżki tak jak dla komend jednak uznałem to za zbędną komplikacje i zostało mi to co widać. Nie będę zamieszczał tym razem implementacji gdyż jest trywialna (jest na githubie) i opiera się na jednym polu zawierającym typ eventa i listę instancji klas nasłuchujących, które niestety musiałem zapakować w object:

private static Dictionary<Type, List<object>> _consumersAsync = new Dictionary<Type, List<object>>();

Dla konsumenta przygotowałem interfejs:

public interface IConsumerAsync<TEvent> where TEvent : IEvent
{
    Task HandleAsync(TEvent eventMessage);
}

Ponieważ spora część moich zdarzeń do będą CRUDowe informacje na temat dodania, aktualizacji lub usunięcia elementu dodałem bardzo przydatną uniwersalną klasę generyczną:

public class Event<TData> : IEvent where TData : DataModelBase
{
    public TData Item { get; protected set; }

    public EventAction Action { get; protected set; }

    public Event(TData item, EventAction action)
    {
        this.Action = action;
        this.Item = item;
    }
}

public enum EventAction
{
    CREATE = 1,
    UPDATE= 2,
    DELETE = 3
}

Kolejna porcja mojego CQRSa za nami, jeszcze trochę do omówienia zostało ale to w kolejnych postach, do przeczytania jutro!