Data. Do you keep it right?



Piotr Przybył
piotr[at]przybyl.org
piotrprz
WrocławJUG, 22 V MMXVIII

© 2018 Piotr Przybył. Licensed under Simplified BSD license.

4 choroby

DDDoza pospolita

biegunka regeksowa

syndrom nie-zrobione-u-nas

stringoza złośliwa

CAVEAT AVDITORES

Legendarne WeWy

Von Neumann Architecture

https://www.computerscience.gcse.guru/theory/von-neumann-architecture

Von Neumann Architecture

http://www.derbildungsblog.ch/4-fachliche-fitness-sich-die-dinge-zu-eigen-machen-weil-verstehen-spass-macht/

Chicken

https://starecat.com/plowing-fields-chicken-hen/

Choroba pierwsza:

DDDoza pospolita

łac. DDDosis silvestris

PIT
Helion

https://helion.pl/users/rejestracja.cgi

Cooland

http://www.coolandsimple.pl/rejestracja/

Fest

http://www.fest.olsztyn.pl/register

Mototurysta

http://www.moto-turysta.pl/rejestracja.html

Poczta Polska address sample Poczta Polska address sample

https://www.poczta-polska.pl/hermes/uploads/2013/10/Poradnik-nt.-poprawnego-adresowania-2016-12-30.pdf

Poczta Polska address sample

https://www.poczta-polska.pl/hermes/uploads/2013/10/Poradnik-nt.-poprawnego-adresowania-2016-12-30.pdf

Selgros address

https://www.selgros.pl/kontakt/wroclaw-dlugoleka

Selgros invoice
Selgros address

https://www.google.pl/maps/search/ul.+Wroc%C5%82awska/@51.1583222,17.1091039,12z/data=!3m1!4b1

Piotr Przybył

ul. Polna 1A

55-095 MIRKÓW

Field street

https://www.google.pl/maps/search/polna/@51.1611543,17.1445451,13z

Długołęka Commune Address

http://gmina.dlugoleka.pl/kontakty/

PKO address

https://inteligo.pl/client/open_account_1/submit

Tutaj odpuszczamy drugi koniec systemu...

Co się będziemy babrać ;-)

Istnieją państwa bez ulic

Istnieją państwa bez PNA

Dlaczego???

(5 WHY principle)

DDD
Event sourcing

Po co? Na co? W jakim celu?

okolicznik celu

Poczta Polska druk 11

Poczta Polska S.A. nr 11

Allegro address

http://allegro.pl

Recepta:

Nie wycieraj sobie gęby DDD.

(Zwłaszcza, jeśli nie wiesz, o czym mówisz.)

Choroba druga:

biegunka regeksowa

łac. Diarrhoea regexis

To może pora na pocztę
e-lektroniczną

Orange email

https://www.orange.pl/rejestracja.phtml?_DARGS=/ocp/gear/profile/rwdRegistration/registration_form.jsp.registration-form

PUE

https://www.zus.pl/portal/riu/riuRejFormularze.npi

Email in Starostwo of Kępno

https://www.powiatkepno.pl/


function validateEmail(email, form){
		if(email.length == 0){
				return true;
		}
		if (email.match("^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$")) {
			return true;
		}
		form.wynik.value = "Niepoprawny format adresu email";
		return false;
}
					

https://rezerwacja.powiatkepno.pl:8443/WAN/js/form_validate.js

Garść poprawnych adresów e-mail

email@domain.com
firstname.lastname@domain.com
email@subdomain.domain.com
firstname+lastname@domain.com
email@123.123.123.123
email@[123.123.123.123]
"email"@domain.com
1234567890@domain.com
email@domain-one.com
_______@domain.com
email@domain.name
email@domain.co.jp
firstname-lastname@domain.com
					

http://codefool.tumblr.com/post/15288874550/list-of-valid-and-invalid-email-addresses

Garść niepoprawnych adresów e-mail

plainaddress
#@%^%#$@#$@#.com
@example.com
Joe Smith 
email.example.com
email@example@example.com
.email@example.com
email.@example.com
email..email@example.com
あいうえお@example.com
email@example.com (Joe Smith)
					

http://codefool.tumblr.com/post/15288874550/list-of-valid-and-invalid-email-addresses

Email in ePUAP

https://epuap.gov.pl/wps/portal/rejestracja-konta

A może RFC?

RFC 5321, RFC 5322, RFC 6531, itp.

A może któryś serwer po drodze nie wspiera?

A może test MX/SMTP?

A może skrzynki nie ma?

A może może zatrzyma "antywirus"?

To może "walidacja" URLa?


$match = preg_match("'\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))'", $url);
if ($match) {
	return true;
}
return false;
					

https://github.com/pfsense/pfsense/blob/5fed4bf20fab6aeb3a4e2556d684acb4a93bfd9f/src/etc/inc/util.inc#L2300

Co ją przejdzie?

"s:/example.com" "_:/example.com" "www.example.com"
" http://example.com" "this is madness _:/sparta.com"

Po co? Na co? W jakim celu?

okolicznik celu

Jeśli masz problem i rozwiążesz go regeksem...

to masz dwa problemy.

(Nie dotyczy Perla.)

Recepta:

Jeśli zbierasz adresy e-mail, żeby coś na nie wysyłać...

to może wyślij pierwszy do weryfikacji.

Choroba trzecia:

syndrom nie-zrobione-u-nas

łac. Syndroma non-fecit-a-nobis


CREATE TABLE jakas-wazna-tabela {
  id long,
  utworzono long
}
					

CREATE TABLE jakas-wazna-tabela {
  id long,
  utworzono varchar(20)
}
					

CREATE TABLE jakas-wazna-tabela {
  id long,
  utworzono_data long,
  utworzono_godzina int
}
					

Dlaczego nie po prostu


CREATE TABLE jakas-wazna-tabela {
  id long,
  utworzono timestamp with time zone
}
					

private static DateTime? ParseDate(string raw)
{
    // YYYY-MM-DDTHH:MM:SSZ
    if (String.IsNullOrEmpty(raw)) return null;
    bool utcInd = false, lenGood;
    if (raw.Length == 10) raw += "T00:00:00";
    if (raw.Length == 20 && (raw[19] == 'Z' || raw[19] == 'z')) { utcInd = true; lenGood = true; } else lenGood = raw.Length == 19;
    if (!lenGood) throw new FormatException();
    int year, month, day, hour, minute, second;
    try
    {
        year = Int32.Parse(raw.Substring(0, 4), _formatProvider);
        month = Int32.Parse(raw.Substring(5, 2), _formatProvider);
        day = Int32.Parse(raw.Substring(8, 2), _formatProvider);
        hour = Int32.Parse(raw.Substring(11, 2), _formatProvider);
        minute = Int32.Parse(raw.Substring(14, 2), _formatProvider);
        second = Int32.Parse(raw.Substring(17, 2), _formatProvider);
    }
    catch { throw new FormatException(); }
    return new DateTime(year, month, day, hour, minute, second, utcInd ? DateTimeKind.Utc : DateTimeKind.Unspecified);
}
					

http://thedailywtf.com/articles/Stringify-All-the-Things!

...bo żaden projekt nie jest dość badziewny w swojej badziewności...

bez własnego parsera dat.

Gdzieś na http://thedailywtf.com

Baza w bazie


CREATE TABLE meta-tabela-do-wszystkiego {
  id long,
  col01 varchar(2000),
  col01_type varchar(20),
  col02 varchar(2000),
  col02_type varchar(20),
  ...
}
					

Generowanie CSV w bazie


 SELECT to, tamto, siamto FROM tabelaX JOIN dużo_metadanych
 -- wygeneruj nagłówek
  FOREACH ROW
    FOREACH COLUMN
    IF typ_kolumny = 'timestamp'
      THEN formatuj_datę(kolumna)
    ELSE
      formatuj_varchar(kolumna) -- "wyeskejpuj jeszcze"
    END;
  -- nowe linie, formatowanie, itp.
					

Generowanie CSV z bazy


psql -c "COPY ( SELECT * FROM TABLE ORDER BY id limit 10 ) TO STDOUT WITH CSV HEADER " > CSV_FILE.csv
					

http://blogs.harvard.edu/dlarochelle/2011/12/11/outputing-to-csv-in-postgresql/

Rolling your own [encryption]

https://twitter.com/MisterCh0c/status/951772327049646080

Jeśli świerzbią cię palce, żeby

po swojemu parsować daty

implementować bazę w bazie

napisać resolver DNS

hashować hasła

generować CSV

to wiedz, że ktoś to już kiedyś napisał.

Prawdopodobnie lepiej.

Recepta:

Gdy masz w ręku złoty młotek i chcesz wbijać wszystko, nie tylko gwoździe...

poproś kumpla, żeby cię trzepnął.

Choroba czwarta:

stringoza złośliwa

łac. Stringoza malignum


CREATE TABLE klienci {
  id char(36) CHECK  --regex sprawdza format UUID,
  nazwa varchar(200),
  nip long,
  data_utworzenia varchar(40),
  stan_konta double CHECK --sprawdza precyzję
}
					

class Klient {
  String id;
  String nazwa;
  Long nip;
  String data_utworzenia;
  Double stan_konta;
}
					

A potem w każdej metodzie...


function operacjaZNipem(Long nip) {
  if (!ValidationUtils.nipPoprawny(nip)) {
    throw new IllegalArgumentException("Niepoprawny NIP!");
  }
  // logyka byznesowa
}
function operacjaZKlientem(String id) {
  if (!ValidationUtils.idPoprawny(id)) {
    throw new IllegalArgumentException("Niepoprawne ID!");
  }
  // logyka byznesowa
}
					

Spróbujmy jeszcze raz...


CREATE TABLE klienci {
  id UUID,
  nazwa varchar(200),
  nip char(10) CHECK --checksum,
  data_utworzenia timestamp with time zone,
  stan_konta money
}
					

class Klient {
  UUID id;
  String nazwa;
  NIP nip;
  Instant data_utworzenia;
  MonetaryAmount stan_konta;
}
					

A może jeszcze lepiej...?


class Klient {
  IDKlienta id;
  String nazwa;
  NIP nip;
  Instant data_utworzenia;
}
					

A potem w każdej metodzie...


function operacjaZNipem(NIP nip) {
  notNull(nip, "nip");
  // logyka byznesowa
}
function operacjaZKlientem(IdKlienta id) {
  notNull(id, "id klienta");
  // logyka byznesowa
}
					

Unikamy absurdów API


klient.id.charAt(0);
(klient.id + " " + klient.nip).getBytes("UTF-8");
//jeśli ID jest np. longiem i data utworzenia też timestampem w longu
if (Math.min(klient.id, klient.data_utworzenia)) {}
					

Przedwczesna optymalizacja przyczyną wszelkiego zła.

Obiekty mutowalne to też optymalizacja.

"Łatwo generować JSONa" to nie argument.

Tests

https://twitter.com/ZackKorman/status/993054858344304641

Tests

https://twitter.com/mariofusco/status/993367261506224129

Recepta:

Za każdym razem, gdy w Javie trzymasz dane w Stringu*

zastanów się

czy nie robisz z Javy PeHaPa.

Podsumowanie

Data. Do you keep it right?


Dzięki bardzo.


Bądźcie zdrowi!

Piotr Przybył
piotr[at]przybyl.org
piotrprz
WrocławJUG, 22 V MMXVIII
https://bit.ly/2LhTLo9
qr