niedziela, 23 kwietnia 2017

Teoria rozbitych okien

"koncepcja w kryminologii i socjologii miasta zakładająca, że brak reakcji na łamanie mniej ważnych norm społecznych, np. tłuczenia szyb w oknach w danej dzielnicy, sprzyja wzrostowi przestępczości i łamaniu innych norm na zasadzie zaraźliwości." (pl.wikipedia.org)

Broken windows

Mówiąc krótko jeśli w otoczeniu jest bałagan, wandalizm to tendencja do łamania norm społecznych zwiększa się. Brzmi znajomo? Pewnie każdy z nas spotkał się z widokiem takich osiedli, budynków itp. Czy na co dzień my jako programiści spotykamy się z tym problemem w pracy? Jak najbardziej! Taka dzielnica to może być nasza aplikacja a budynek projektem, modułem. A akty wandalizmu? Nieczytelny kod, mieszanie różnych notacji, brak obiektowości, łamanie różnych zasad solid, dry, kiss itd. Można by wymieniać wiele takich "grzeszków" programistów ale chyba największym z nich jest przyzwolenie na ich powielanie zgodnie z zasadą, że skoro i tak już jest bałagan, to moja zła zmiana wiele więcej nie popsuje. Często  jest to także związane z tym, że przystosowujemy się do otoczenia i jest ogólne przyzwolenie na taki stan. Jest jak jest, po co zmieniać ... Ale wiemy do czego takie podejście prowadzi.



Temporary fix



Drobna poprawka do kodu

Otrzymujesz zadanie wprowadzenia drobnej poprawki do kodu. Znalazłeś już klasę, metodę do zmiany. Przeglądasz istniejący kod i w tym momencie dość często stajesz przed wyborem czy wykonać ją tak aby zostawić jak najmniej zmian i mieć problem z głowy czy dokonać przy okazji drobnej refaktoryzacji. Ja stosuje zasadę "pozostaw kod lepszym niż go zastałeś". To mogą być naprawdę drobne rzeczy, które można zrobić "przy okazji" i często są one automatyzowane przez IDE, np.:
  • usunięcie zbędnych pustych linii, poprawienie wcięć
  • usunięcie referencji do nieużywanych modułów
  • usunięcie zbędnych komentarzy
  • usunięcie nieużywanego kodu
Kolejny przykład z jakim spotykam się na co dzień to notacja węgierska. Projekt ma kilka lat i tak od początku był pisany. Ale zasady się zmieniły, dziś już jej nie stosujemy. Więc jeśli trafiam w miejsce gdzie występuje i mam wprowadzić tam zmiany to nie rozbijam kolejnego okna tylko piszę nowy kod wg nowych zasad.

Nie było testów

Dodajesz lub modyfikujesz metodę i widzisz, że nie posiada ona testów jednostkowych to dodaje je. Nowa klasa? tym bardziej! To, że do tej pory niektórzy ich nie pisali nie oznacza, że masz być kolejnym "wandalem".

Podsumowując

Czasem trudno jest wyłamać się i zacząć wprowadzać zmiany. Jeśli nie czujesz się na siłach poproś o pomoc team leadera. Uważaj także aby nie stać się przez przypadek rewolucjonistą i nie dokonać przewrotu w projekcie. Pamiętaj "small changes can make a big difference"

niedziela, 16 kwietnia 2017

Personifikacja kodu

Dziś krótko o czymś czego osobiście nie lubię i drażni mnie w pracy codziennej - uosabianiu kodu, przypisywania go oraz jego działania konkretnej osobie. Przykłady: "Ta twoja metoda", "Ty tu robisz ...", "Ty tu pobierasz ..." itd. itp. Nic nie jest moje, nic nie pobieram, nic nie przetwarzam - to wykonuje kod, który jest nasz wspólny. Takie zwroty odbieram jako krytykę osoby a raczej powinno się tu mówić o kodzie jego złym działaniu o tym jak go zmienić. Analogicznie mam z swoimi dziećmi - wskazuje na złe zachowanie a nie krytykuje dziecko. Druga sprawa, która się z tym wiąże abyś ty jako deweloper nie przywiązywał się emocjonalnie do kodu, identyfikował się z nim. Wtedy może się okazać, że jego krytykę będziesz odbierał zbyt personalnie.

sobota, 15 kwietnia 2017

Dogevents - Azure deploy

To był tydzień pt. "U mnie działa". Jak zwykle to sprawdzone w boju powiedzenie dało się we znaki. Wszystko za sprawą wdrożenia wszystkich komponentów związanych z projektem Dogevents do chmury Azure. I bynajmniej nie była to sprawka Azure tylko złego podejścia w projekcie i braku zaimplementowanych pewnych mechanizmów takich jak np. konfiguracja. I jeszcze jedno, dlaczego Azure? Bo nie mam możliwości pracy z nim na co dzień a zawsze chciałem go wypróbować, aby zdobyć jakieś doświadczenie i dlatego, że mam tam jakąś kasę do wykorzystania 😁

Architektura



Powyższy diagram przedstawia aktualną architekturę aplikacji, jakie komponenty Azure zostały wykorzystane. Było to moje pierwsze spotkanie z chmurą Microsoftu więc nie obyło się bez problemów ale już teraz mogę powiedzieć, że jest to bardzo wygodne rozwiązanie a automatyczny deploy z github'a to jest po prostu bajka!

Wdrażanie poszczególnych składników zacząłem od backendu czyli tych podstawowych rzeczy, które są wymagane do działania. Opiszę każdy składnik ogólnie. Szczegółowy opis znajdzie się w kolejnych postach.

#1 Resource Group

Coś co nie zostało wymienione na diagramie a z czego korzystają wszystkie usługi Azure. Grupa zasobów pozwala m.in na monitorowanie, zarządzanie, kontrolę dostępu podpiętych do niej usług. Utworzyłem jedną podstawową "Dogevents" do której podpiąłem wszystkie swoje usługi.
Dogevents - Azure resource group


#2 Mongo DB

Azure udostępnia wiele usług bazodanowych - ja wybrałem tą najprostszą "Database as a Service for MongoDB". Mamy całkiem fajny panel do zarządzania m.in kolekcjami, przeglądania danych, wykonywania zapytań. Jest także sekcja "Connection string", z której możemy pobrać konfigurację połączenia do bazy danych.

Azure - MongoDB connection string

#3 Dogevents API + Mgmt

Ta część dostarcza dla mnie panel do zarządzania aplikacją oraz udostępnia REST API dla warstwy klienckiej. Wykorzystana technologia ASP.NET Core MVC. Utworzyłem nową aplikację typu Web App i skonfigurowałem automatyczny deploy z github'a. Ta opcja to jest po prostu petarda! Żadnego ręcznego przepychania plików (ech te czasy FTP ;) ) - wszystko wykonuje się z automatu.

Azure - deployment options

Azure - deployment from github

To jest podstawowa konfiguracja, która wystarcza do wykonania deploy z repoytorium na github. Jest możliwość bardziej zaawansowanej kontroli poprzez skrypty .deployment. Dość przystępnie zostało to opisane tutaj

#4 Dogevent web client

Kolejny już składnik to aplikacja kliencka wykonana z użyciem Node.js,Vue.js oraz Webpack. Konfiguracja tej usługi przysporzyła mi najwięcej problemów ale to raczej z racji małej jak dotąd znajomości środowiska Node.js i Azure. Największym problemem było to, że ta aplikacja zawiera się w tym samym repozytorium co Dogevents API i deploy uparcie wdrażał właśnie ten projekt. Z pomocą przyszedł Scott Hanselmann i jego post "Deploying TWO websites to Windows Azure from one Git Repository". Po wskazaniu, który konkretny projekt (możliwe jest wskazanie na .csproj lub folder) ma zostać wdrożony problem został rozwiązany.
Azure - deploy specific project
Na chwilę obecną brakuje jeszcze jednej rzeczy. Tak aby npm build wykonywał się po stronie Azure. Do tego będę potrzebował wspominanego skryptu .deployment. 

#5 DNS zone

Strefa dns potrzebna była aby podpiąć domenę dogevents.pl pod właściwy adres. To była dla mnie jedna z prostszych rzeczy może dlatego, że pamiętam swoje początki w IT i wszelkie babranie się z Bind pod linuksem 😃Cała procedura zamyka się w kilku krokach:
  1.  Zmiany serwera DNS u rejestratora domeny na serwery nazw Azure:  ns1-02.azure-dns.com, ns2-02.azure-dns.net
  2. Utworzenie strefy DNS
Azure - Create DNS zone

Ta operacja spowoduje przeniesienie domeny na serwer Azure. Aby wskazywała na konkretny zasób - aplikację web, trzeba utworzyć rekord typu A wskazujący na adres IP. Można go odnaleźć w sekcji "Custom domains" aplikacji.
Azure - IP adress A record
 Wracamy do konfiguracji utworzonej strefy. Na tym etapie należy wykonać dwie czynności:

  1.  Utworzenie rekordu typu A  (Opcja "+ Record set")
    Aby utworzyć podstawowy rekord  wystarczy w pole "IP Address" wpisać adres IP naszej aplikacji. Gdybyście byli zainteresowani co oznaczają pozostałe opcję lub co nam daje pole "Name" odsyłam do internetów
    Azure - create A record

  2. Utworzenie rekordu typu TXT wksazującego na pierwotną domenę naszej aplikacji - tą, którą zawsze generuje Azure. Ma ona format nazwa_aplikacji.azurewebsites.net
Tak przygotowaną strefę DNS można podpiąć do aplikacji. Opcja można znaleźć w konfiguracji jako "Custom domains". Niestety nie jest ona dostępna dla zasobów typu "free" - bezpłatnych.

Podsumowanie

Naprawdę w wielkim skrócie opisałem moją drogę do wdrożenia wszystkich składników projektu Dogevents do chmury Azure. Niestety czasu jest mało a implementacja musi jakoś iść w parze z blogiem :) Gdybyście mieli uwagi, podpowiedzi co można by było rozwiązać inaczej zapraszam do komentowania.

Przydatne linki:

niedziela, 9 kwietnia 2017

Vue.js filters - formatowanie daty

Filtry w Vue.js są jak helper'y czy utils'y w C#. Takie pomocnicze metody, które przydają się w wielu miejscach w aplikacji i pozwalają uniknąć duplikowania kodu. Podstawowe informacje na ich temat można przeczytać tutaj lub w formie tutoriala na youtube.
Pierwszy filtr, który zaimplementowałem w aplikacji Dogevents wiązał się z wyświetlaniem daty wydarzenia. Każde wydarzenie składa się z dwóch pól typu data/czas określających jego początek oraz koniec:

{
  "start_time": "2017-05-06T14:30:00+0200",
  "end_time": "2017-05-06T19:30:00+0200",
}

Przedstawiony format choć jest jak najbardziej prawidłowy w kontekście wymiany, przechowywania danych o tyle jego czytelność jest niska - nie jest "user friendly" :)

Prócz bardziej przyjaznego wyświetlania tych informacji chciałem uzyskać jeszcze kilka dodatkowych efektów:
  • Jeśli wydarzenie odbywa się w jednym dniu wyświetlić tylko raz datę i godzinę rozpoczęcia + godzinę zakończenia, np. 2017-02-04 10:00 - 14:00
  • Jeśli wydarzenie trwa więcej niż jeden dzień i nie jest na przełomie miesiąca wyświetlić jego datę jako 5 - 8 Maj 2017
Wyświetlanie daty wydarzenia z użyciem filtra 

Działający przykład udostępniłem na JSFiddle

Na początek wywołanie filtra toEventDate z poziomu szablonu:

<div id="app">
    <p>{{ { start_time: start_time, end_time: end_time } | toEventDate}}</p>
</div>

Wykonuję tu jedną dodatkową rzecz - opakowanie w obiekt dwóch właściwości start_time, end_time tak aby móc przekazać je jako jeden parametr do funkcji filtra. Następnie rejestracja globalnego filtra:


Vue.filter('toEventDate', function(eventDate){
 //some logic here ...
}

I ostateczna funkcjonalność:


Vue.filter('toEventDate', function(eventDate) {
  let startTime = new Date(eventDate.start_time);
  let endTime = new Date(eventDate.end_time);
  let locale = 'en-EN';
  let dateFormatOptions = {
    day: 'numeric',
    month: 'long'
  };
  let timeFormatOptions = {
    hour: 'numeric',
    minute: 'numeric'
  };

  if (startTime.toLocaleDateString() === endTime.toLocaleDateString())
    return startTime.toLocaleDateString(locale, dateFormatOptions) + ' ' + startTime.toLocaleTimeString(locale, timeFormatOptions) + ' - ' + endTime.toLocaleTimeString(locale, timeFormatOptions);

  if (eventDate.start_time.substring(0, 7) == eventDate.end_time.substring(0, 7))
    return startTime.getDate() + ' - ' + endTime.getDate() + ' ' + startTime.toLocaleDateString(locale, {
      month: 'long'
    })

  return startTime.toLocaleDateString(locale, dateFormatOptions) + ' - ' + endTime.toLocaleDateString(locale, dateFormatOptions)
});

piątek, 7 kwietnia 2017

#3 Recepta na - Unit Test + Object Builder

Dzisiejsza recepta będzie dotyczyła obiektów i sposobu ich konstruowania na potrzeby testów jednostkowych. Zastosowany wzorzec nie ogranicza się tylko do projektów testowych i jak najbardziej można go używać w innych warstwach.

Builder Design Pattern

Jak głosi wikipedia i inne źródła wzorzec budowniczy (prawda, że fajne tłumaczenie przyjęto 😃) wspomaga konstruowanie obiektów. Pozwala odseparować reprezentację obiektu (pola, właściwości) od pocesu jego konstrukcji. Tyle z teorii.

Kiedy używać

Wtedy kiedy w wielu miejscach w aplikacji tworzymy tą samą instancję obiektu o podobnych właściwościach. Mówiąc wprost jeśli widzisz, że klepiesz po raz n-ty ten sam kod aby utworzyć jakiś obiekt to jest to dobra wskazówka aby rozważyć ten wzorzec. Najczęściej ta sytuacja ma miejsce w testach jednostkowych gdzie każdy test jest wykonywany w izolacji i z założenia powinien operować na własnych instancjach obiektu.

Kodzimy

Na początek model obiektu, który posiada wiele właściwości - jest złożony. Klasyczny przykład zamówienia, które zawiera informację o kliencie, metodzie wysyłki i przedmiotach zakupu.


public class Order
{
    public OrderInfo OrderInfo { get; private set; }
    public ShipmentMethod Shipment { get; private set; }
    public ICollection<OrderItem> Items { get; private set; }
}

public class OrderInfo
{
    public Customer Customer { get; private set; }
}

public class Customer
{
    public Address Addres { get; private set; }
}

public class Address { }

public class OrderItem { }

public class ShipmentMethod { }

Utworzenie takiego obiektu od zera wiązałoby się z zainicjowaniem wielu pól i gdyby powtarzać tą czynność dla każdego testu powstało by wiele duplikowanego kodu. Taki kod określa się mianem "Boilerplate code" - takie obiekty potrzebujemy wielu miejscach (dla każdego testu) a nie wiele wnoszą. Dodatkowo poprzez brak enkapsulacji zmiana struktury którejś z klas Order, OrderInfo itd. skutkowała by poprawkami w wielu miejscach.


[Test]
public void My_First_Test()
{
    //Prepare some order
    var order = new Order();
    order.OrderInfo = new OrderInfo
    {
        Customer = new Customer
        {
            //fill customer properties ...
        }
    };
    order.Shipment = new ShipmentMethod
    {
        //fill shipment info ...
    };
    order.Items = new List<OrderItem>
    {
        //add some items to order ...
        new OrderItem { },
        new OrderItem{ }
    };

    //Act

    //Assert
}

Wyjście zastępcze

W niektórych przypadkach możemy dojść do wniosku, że nasz obiekt będzie wyglądał zawsze tak samo i nie potrzebujemy dodatkowych możliwości jego "konfiguracji". Wtedy możemy utworzyć metodę, która wprost będzie zwracała taką instancję coś na wzór:

[Test]
public void My_First_Test()
{
    var order = GetSampleOrder();

    //Act

    //Assert
}

[Test]
public void My_Second_Test()
{
    var order = GetSampleOrder();

    //Act

    //Assert
}

private Order GetSampleOrder()
{
    //Prepare some order
    var order = new Order();
    order.OrderInfo = new OrderInfo
    {
        Customer = new Customer
        {
            //fill customer properties ...
        }
    };
    order.Shipment = new ShipmentMethod
    {
        //fill shipment info ...
    };
    order.Items = new List<OrderItem>
    {
        //add some items to order ...
        new OrderItem { },
        new OrderItem{ }
    };

    return order;
}

Jednak jeśli zaczniemy rozmnażać te metody bo jednak przyda się innaczej wyglądające zamówienie to wtedy czas na:

OrderObjectBuilder

W którym udostępnimy możliwość konstruowania różnych reprezentacji zamówienia. Dodatkowo ta implementacja będzie wykorzystywała tzw. "method chaining". Pozwolę sobie na bardzo opisowe nazwy metod tak aby wprost pokazać wam ideę.

Krok #1

Utworzenie klasy OrderObjectBuilder z dwiem metodami:
  • CreateOrder() - tworzy instancje zamówienia - najprostsza możliwa reprezentacja. Zwróć uwagę na "return this". To wspomniany "method chaining". Zwracamy instancję klasy OrderObjectBuilder dzięki czemu będziemy mieli możliwość wywoływania kolejnych jej metod po "kropce" - przykład najlepiej to zaprezentuje.
  • Build() - metoda, która zwraca instancję zamówienia. Tą metodę wołamy na samym końcu.

public class OrderObjectBuilder
{
    private Order _order;

    public OrderObjectBuilder CreateOrder()
    {
        _order = new Order();
        return this;
    }

    public Order Build()
    {
        return _order;
    }
}

Przykład użycia:

OrderObjectBuilder _orderBuilder = new OrderObjectBuilder();

[Test]
public void My_First_Test()
{
    var myOrder = _orderBuilder
                    .CreateOrder()
                    .Build();
}

"Method chaining" umożliwił zawołanie metody Build() bezpośrednio po CreateOrder().

Krok #2

Dodaj kolejne metody, które posłużą do utworzenia twojego obiektu:


public OrderObjectBuilder WithCustomer()
{
    //create here sample customer
    return this;
}

public OrderObjectBuilder WithCustomer(Customer customer)
{
    _order.OrderInfo.Customer = customer;
    return this;
}

public OrderObjectBuilder WithShipment()
{
    //create sample shipment method
    return this;
}

public OrderObjectBuilder WithDPDShipment()
{
    //set DPD shipment
    return this;
}

public OrderObjectBuilder WithDHLShipment()
{
    //set DHL shipment
    return this;
}

public OrderObjectBuilder WithItem()
{
    //add sample item to order
    _order.Items.Add(new OrderItem());
    return this;
}

public OrderObjectBuilder WithItem(OrderItem orderItem)
{
    _order.Items.Add(orderItem);
    return this;
}

public Order Build()
{
    return _order;
}

Dzięki takiej implementacji możemy tworzyć w prosty i czytelny sposób wiele przypadków testowych naszego zamówienia:

var myOrder = _orderBuilder
                    .CreateOrder()
                    .WithCustomer()
                    .WithDHLShipment()
                    .WithItem() //some random item
                    .WithItem(new OrderItem()) //particular order item
                    .Build();

var orderWithNoItems = _orderBuilder
                            .CreateOrder()
                            .WithCustomer()
                            .WithDPDShipment()
                            .Build();

var orderWithNoShipment = _orderBuilder
                            .CreateOrder()
                            .WithCustomer()
                            .WithItem()
                            .Build();

Podsumowanie

Dzięki wprowadzeniu ObjectBuilder'a:
  • Uzyskaliśmy separację reprezentacji obiektu od jego konstruowania
  • Pozbyliśmy się zbędnego, niewiele wnoszącego do przypadku testowego kodu
  • Enkapsulacja zabezpieczy nas przed przyszłymi zmianami
  • Method chaining wprowadził czytelne API

poniedziałek, 3 kwietnia 2017

♥ Vue.Js

Vue.js jest dosyć świeżym frameworkiem ale widać, że systematycznie zyskuje swoje grono fanów. W aktualnej edycji konkursu naliczyłem 13 śmiałków, którzy zadeklarowali jego użycie. Zrobiłem nawet małe zestawienie jak to się ma do Angular oraz React:


Jak widać jestem w zdecydowanej mniejszości ale to nie szkodzi wręcz otwiera pole do popisu ;).

Pierwsze spotkanie z Vue.js

O Vue.js dowiedziałem się przypadkiem kilka miesięcy temu. Już nawet nie pamiętam czy był to podcast czy jakiś tweet - nieważne. Był to wtedy dla mnie kolejny framework js. Coś jak z tym żartem kiedy backendowiec wita się w pracy z frontend'owcem i mówi "Co tam, jest jakiś nowy framework?". Poświęciłem chwilę na "get started" i już wiedziałem, że to to! Dla mnie jako osoby zasiedzianej w backend wyglądało to bardzo czytelnie, prosto. Szukałem czegoś co przy nie dużym nakładzie pozwoli uzyskać model binding po stronie html'a. I do tego szablony... coś pięknego :)
Potwierdzeniem tego, że próg wejścia nie jest duży może być to, że w projekcie mam już kilka komponentów, pobieranie danych z rest'a a nad CSS'ami dalej k***a siedzę i nic nie "fituje" 👿

Od czego zacząć

Prócz przewodnika na stronie projektu, polecam kanał na youtube Mindspace - Vue.js
Jest tam kilkanaście krótkich odcinków, które dość dobrze wprowadzają w działanie Vue.js
Póki co materiałów nie jest dużo. Hashtag #vuejs na StackOverflow świeci pustkami ... może dlatego, że nie ma z nim problemów :P
Jeśli chodzi o szablony projektów to mam dwa źródła:
Plus do tego cała mas projektów, do których linki są zebrane w repozytorium awesome-vue.

Z ostatniej chwili! Jeden z uczestników DSP2017 +webMASTAH  rozpoczął kurs więc może warto także i tam zajrzeć.

Wsparcie IDE

Korzystam z VSCode ale na obecną chwilę nie ma wielu rozszerzeń, które by wspierały w szczególności pisanie komponentów vue, gdzie w  jednym pliku umieszczony jest szablon html, css i javascript. Jest dostępny jeden syntax highlighter ale posiada pewien problem:

Drugie rozszerzenie "Vue 2 Snippets" dodaje zarówno podświetlanie składni jak i snippety i póki co się sprawdza. Za rękę nie prowadzi ale ważne, że nie przeszkadza.
Dziwna sprawa jest z powiązaniem rozszerzenia pliku ".vue" z typem. Powiązanie wprost wprowadza odpowiednie ikony na drzewie projektu ale całkowicie wyłącza intelisense dla html'a, js'a. Po zmianie na "html" jest dobrze.

"files.associations": {
    "*.vue": "html"
}

Z tego co widziałem to chyba najlepsze wsparcie oferuje WebStorm 2017.1, który posiada już wbudowane wsparcie dla Vue.js.

Dogevents client

Do swojego rozwiązania wprowadziłem nowy projekt Dogevents.Web.Client, który będzie aplikacją kliencką.

Moim celem jest aby była ona jak najbardziej responsywna, pisana w podejściu "mobile first" i docelowo stała się Progressive Web Application. Pierwsze dwa założenia są jak najbardziej w zasięgu. To trzecie, o którym postaram się napisać w osobny poście, już nie jest takie trywialne.

Vue.js components

https://vuejs.org/images/components.png

Jedną z wielu zalet Vue.js są komponenty. Można je przyrównać do sekcji na stronie, np. menu, nagłówek, stopka, news'y, tagi. Z założenia są niezależne i mogą być wielokrotnego użytku.
Aktualnie wydzieliłem 3 komponenty:
  • Popular.vue
  • JustAdded.vue
  • Event.vue
Event.vue jest najmniejszym komponentem. Reprezentuje informację związane z wydarzeniem.
komponent Event.vue - widok wydarzenia

Popular.vue agreguje kilka najpopularniejszych wydarzeń i korzysta z komponentu Event.vue do ich prezentacji.


Ostatni - JustAdded, odpowiedzialny za wyświetlenie ostatnio dodanych wydarzeń także wykorzystuje komponent Event.vue.



Co najbardziej mi przypadło do gustu to separacja komponentów i możliwość ich szybkiej podmiany na inną instancję. Pozwala to na eksperymenty z widokiem i dodatkowo adhoc jest odzwierciedlane w całej aplikacji. Cały kod można podejrzeć na github.