Microsoft SQL i docker i…Linux ;-)

Przyznaje, że tytuł może się wydawać dosyć prowokacyjny, ale nie da się zaprzeczyć, że Microsoft coraz bardziej otwiera się na Linuxa. Wydaje się, że obecny kurs Microsoftu to odejście od traktowania go niczym “raka” (https://pl.wikiquote.org/wiki/Steve_Ballmer). Więcej na temat otwarcia się Microsoftu na środowisko open source można przeczytać na https://www.spidersweb.pl/2016/11/linux-foundation-microsoft.html. W każdym razie dzisiejszy wpis będzie o moich przygodach z skonfigurowaniem Dockera ( https://www.docker.com/ ) pod bazę Microsoft SQL Server dla jednego z moich projektów. By to zrobić użyje jeszcze docker-compose, tak aby móc wygodnie sterować wszystkimi wymaganymi kontenerami przypisanymi do projektu. Natomiast w celu utworzenia przykładowych baz danych i użytkowników trzeba będzie przebudować obraz sql servera.

Plik docker-compose.yml może wyglądać tak

version: '2.0'

services:
    mssql:
        build:
            context: .
            dockerfile: docker/Dockerfile-mssql
        image: mymssql
        networks:
            protectedNetwork:
                ipv4_address: 192.168.56.5

networks:
  protectedNetwork:
    ipam:
     config:
       - subnet: 192.168.56.0/24
         gateway: 192.168.56.99 # disable traffic

Wszystkie artefakty konieczne do budowy obrazu będę trzymał w katalogu docker/. W projekcie potrzebna jest wewnętrzna sieć (tj. protectedNetwork) do współdzielenia zasobów między kontenerami.

Plik docker/Dockerfile-mssql

FROM microsoft/mssql-server-linux

RUN mkdir -p /opt/mssql-scripts
ADD docker/mssql.sql /opt/mssql-scripts

ENV SA_PASSWORD tajneHaslo
ENV ACCEPT_EULA Y

RUN ( /opt/mssql/bin/sqlservr --accept-eula & ) | grep -q "Service Broker manager has started" \
    && /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'tajneHaslo' -i /opt/mssql-scripts/mssql.sql \
    && pkill sqlservr 

W tym miejscu instruujemy Dockera by pobrał utworzony przez Microsoft gotowy obraz SQL Servera, utworzył katalog i skopiował do niego plik z instrukcjami sql (mssql.sql, zawartość podana niżej). By obraz był gotowy należy jeszcze utworzyć zmienne środowiskowe z hasłem i akceptacją umowy (SA_PASSWORD, ACCEPT_EULA). Ostatnia linia wymusza uruchomienie Sql Servera ( w trakcie budowy obrazu), aby załadować plik sql.

Plik docker/mssql.sql

CREATE DATABASE databasemock;
GO
CREATE LOGIN databasemockLogin WITH PASSWORD = 'databaseMock1#';
USE databasemock;
CREATE USER databasemockUser FOR LOGIN databasemockLogin WITH DEFAULT_SCHEMA = databasemock;
GO
GRANT ALL TO databasemockUser;
GO

Plik z instrukcjami SQL zawiera jedynie utworzenie bazy danych i użytkownika z uprawnieniami do niej.

Po zbudowaniu obrazu (docker-compose build) i uruchomienia kontenerów (docker-compose up), w celu podłączenia się do bazy danych można użyć narzędzia sqlcmd. Również jest on dostępny dla Linuxa 😉 https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-tools?view=sql-server-2017.

W uproszczeniu /opt/mssql-tools/bin/sqlcmd -S Adres IP,1433 -U Użytkownik -h -1 -P ‘hasło’ -d NazwaBazayDanych

Parametr -h -1 jest opcjonalny, wyłącza wyświetlanie nagłówów tj. na ekranie będą prezentowane jedynie wyniki zapytań bez dodatkowego ich formatowania.

Polecenia SQL można wysłać z pliku (jako batch) po przez parametr -i nazwapliku, albo wykonać z terminala np. jako skrypt, parametr -Q “Zapytanie SQL”

Właściwie to tyle na dziś. Nie wiem jak Wy, ale ja jestem pod wrażeniem obecną polityką Microsoftu. Oby tak dalej!

Kiedy CQRS a kiedy CRUD ?

Dzisiaj trochę o architekturze systemów informatycznych. Wzorzec Command Query Responsibility Segregation (CQRS) a dobrze już poznany model Create Read Update and Delete (CRUD). Na początek krótka charakterystyka, a w dalszej części spróbuje opisać w jakich przypadkach Fowler uważa CQRS za lepsze rozwiązanie.

CQRS pozwala nam potraktować dane w odmienny sposób niż w tradycyjnym ujęciu CRUD. Dla przykładu składanie modelu z wielu rekordów czy tworzenie jakiegoś wirtualnego rekordu łączącego różne miejsca. W przypadku aktualizacji danych może to być określenie reguł sprawdzenia poprawności tych danych czy też zezwolenie na przechowywanie tylko pewnych ich kombinacji. Widać tutaj, że użytkownik tak naprawdę korzysta z wielu różnych od siebie reprezentacji tych informacji.

Problem w tym, że w takim utartym już podejściu CRUD, gdy istnieje potrzeba rozdzielenia i na nowo zdefiniowania nowego modelu danych to siłą rzeczy sprowadzamy ją do jednego miejsca, która działa jako punkt integracji tych wszystkich koncepcji. Natomiast CQRS rozdziela ten model na modele aktualizacji (Command) i wyświetlania (Query). Fowler jako uzasadnienie tego podziału wskazuje, że w przypadku bardziej skomplikowanych aplikacji, posiadanie wspólnego modelu (dla poleceń i zapytań), czyni kod zbyt złożonym.

Zatem wracając do pytania od którego zacząłem kiedy CQRS a kiedy CRUD ? Zdaniem Fowlera nie powinno się używać CQRS w całej aplikacji, a jedynie w określonych jej częściach (np. BoundedContext w języku DDD). Co więcej owa część (domena) musi dać się zamodelować w taki sposób, inaczej niepotrzebnie zwiększymy złożoność kodu. Zaletą zastosowania CQRS jest też poprawa wydajności aplikacji. Dzieje się tak poprzez podział obowiązków, obie części programu można skalować w sposób od siebie niezależny. Jak również wprowadzając różne strategie optymalizacji czy techniki dostępu do bazy danych.

Reasumując CQRS jest wzorcem, o którym warto sobie przypomnieć, gdy tradycyjny model CRUD zawodzi. Trzeba jednak mieć na uwadze, że jego implementacja nie należy do łatwych. Stosując go jedynie w takich częściach systemu, gdzie wspomniany podział wydaje się być naturalny.

Źródło: https://martinfowler.com/bliki/CQRS.html

set_error_handler jako elegancki sposób na bolączki z PHP

Gdy PHP powstawało nikt nawet nie śnił, że to narzędzie znajdzie zastosowanie w dużych projektach informatycznych aniżeli tylko w prostych skryptach. Pewnym tego pokłosiem jest wprowadzenie mechanizmu wyjątków (https://www.php.net/manual/en/language.exceptions.php) dopiero w wersji 5. Niestety ze względu na kompatybilność sporo wbudowanych funkcji, struktur języka takich jak np. fopen(), mail(), dzielenie przez zero etc. nie zgłasza wyjątków a E_ERROR, E_WARNING.. (https://php.net/manual/en/errorfunc.constants.php).

Jeśli zamiast tego chcielibyśmy otrzymać wyjątek lub inaczej obsłużyć taką sytuacje to trzeba customizować obsługę błędów w PHP właśnie przez set_error_handler(). Poniżej przykład zaczerpnięty z Zend Mail (zendframework/zend-mail/src/Transport/Sendmail.php)

/**
 * Send mail using PHP native mail()
 *
 * @param  string $to
 * @param  string $subject
 * @param  string $message
 * @param  string $headers
 * @param  $parameters
 * @throws \Zend\Mail\Transport\Exception\RuntimeException
 */
public function mailHandler($to, $subject, $message, $headers, $parameters)
{
    set_error_handler([$this, 'handleMailErrors']);
    if ($parameters === null) {
        $result = mail($to, $subject, $message, $headers);
    } else {
        $result = mail($to, $subject, $message, $headers, $parameters);
    }
    restore_error_handler();

    if ($this->errstr !== null || ! $result) {
        $errstr = $this->errstr;
        if (empty($errstr)) {
            $errstr = 'Unknown error';
        }
        throw new Exception\RuntimeException('Unable to send mail: ' . $errstr);
    }
}

Z tego miejsca warto przypomnieć, że gdy już się customizuje error handler to należy zadbać o zapisanie kompletnej informacji o pochodzeniu błędu (komunikat, miejsce wywołania i inne dodatkowe informacje ułatwiające debugowanie).

Więcej w https://www.php.net/manual/en/function.set-error-handler.php

Skąd biorą się wielcy programiści

Dzisiejszy wpis chciałem by był o wzorcu Remote Facade jako uzupełnienie poprzedniego wątku. Jednak nie będzie o tym bo za sprawą Pawła (tego od alpak) wpadł mi artykuł autorstwa Eduards Sizovs, link na końcu. Wyczytałem tam kilka ciekawych sugestii jak powinien wyglądać proces rekrutacji dewelopera, ale też o przeszkodach jakie mogą czyhać w rozwoju samej kariery. Skupmy się na tej drugiej kwestii oraz porozmawiajmy o tym skąd właściwie biorą się ci wielcy programiści.

Każdemu z nas zależy by być w czymś dobrym, by stać się mistrzem w swojej profesji, by okiełznać te wszystkie przeszkody rzucone po drodze i odnieść sukces. Problem w tym jak tego dokonać, kiedy bardziej jesteśmy skupieni na dostarczaniu oprogramowania a w mniejszym stopniu na własnym rozwoju. Wydaje mi się, że najbardziej istotne jest uświadomienie sobie w tym wszystkim, że

“Wielcy programiści są wychowywani a nie zatrudniani”

Wynika z tego, że każdy ma szanse. Trzeba zdekomponować problem na mniejsze składowe. Znaleźdź mentora. Zaplanować swoją karierę…hmm wydaje się to nazbyt proste… Bo co z emocjami? Co z zwątpieniem w swoje możliwości lub wypaleniem? Jak poradzić sobie gdy czujesz, że projekt Cię przerasta? Cóż…dobrze wtedy być w takim zespole, w którym tworzy się miejsca pracy do rozwoju zamiast szukać tego wielkiego programiste…

Link do oryginalnego wpisu https://sizovs.net/2019/04/10/the-best-developers-are-raised-not-hired/

Data Transfer Object – Obiekt Transferu Danych

Co raz cześciej przychodzi mi pisać aplikacje w środowisku rozproszonym to też dzisiejszy, inauguracyjny wpis będzie o Data Transfer Object – Obiekt Transferu Danych, zwany także przez niektórych jako Value Object. W skrócie

Obiekt przenoszący dane pomiędzy procesami, umożliwiający zmniejszenie ilości wywoływanych metod M. Fowler

Tylko co się za tym kryje? Jednym z wielu problemów używania obiektów rozproszonych jest ich koszt wywołania. To czas, który trzeba ponieść, by wywołać zdalną metodę. W przypadku wywołań we wspólnej przestrzeni adresowej danego procesu, nie musimy przejmować się tym czasem. Natomiast sytuacja radykalnie się zmienia, kiedy potrzebujemy wywołać metodę na innym procesorze, w szczególności gdy ów procesor znajduje się na drugim krańcu kuli ziemskiej.

Jednym ze sposóbów radzenia sobię z tym problem jest zmniejszenie ilości wywołań takich metod. Można do tego podejść po przez przesłanie większej ilości danych niż jest to konieczne za jednym razem. Nie jest to jednak takie proste w implementacji. Niektóre języki programowania jak np. Java wymuszają zwrócenie tylko jednej wartości. Dlatego tutaj z pomocą przychodzi nam wzorzec Data Transfer Object, który z pozoru wygląda jak klasa mająca jedynie swoje pola, akcesory i mutatory. Jednakże dodatkowo owa klasa wzbogacana jest o serializacje

Serializacja

To właśnie dzięki niej, DTO jest w stanie przesłać całą swoją zawartość przez sieć w jednym wywołaniu. Natomiast konkretny używany format zapisu zależy już od programów. Wiele języków programowania jak np. PHP czy Java posiadają gotowe do tego mechanizmy. Trzeba jednak pamiętać, aby zarówno klient jak i serwer używały tego samego mechanizmu. Pewnym dylematem jest czy wybrać serializacje do postaci binarnej czy tekstowej np. XML, JSON. Kuszące jest to drugie rozwiązanie ze względu na łatwość odczytu, ale wymaga to większej przepustowości. Innym problemem jest synchronizacja obiektów transferu danych pomiędzy komunikującymi się stronami. Gdy serwer zmienia dane, taka aktualizacja powinna zajść także po stronie klienta…w teorii. W praktyce po prostu dostaniemy błąd.

Jak utworzyć taki obiekt DTO ?

Zdaniem Fowlera takie obiekty nie powinny być częścią dziedziny, ale też obiekty dziedziny nie powinny być zależne od obiektów transferu danych (bo struktura tych drugich może się zmieniać wraz ze zmianami formatów przesyłanych danych). Tutaj Fowler sugeruje użycie “obiektów grupujących” / “asemblerów“, czyli obiektów tworzących DTO używajac interfejsów modelu dziedziny. Takie obiekty grupujące przypominają wzorzec Mapper, mapują obiekt dziedziny na obiekt transferu danych.

Na zakończenie, warto nadmienić, że DTO całkiem fajnie współpracuje z innym wzorcem – Remote Facade, ale o tym kiedy indziej…

Więcej informacji na ten temat w ksiażce “Architektura Systemów Zarządzania Przedsiębiorstwem. Wzorce projektowe“. M. Fowler.