CQRS nauka

EN
  • Rejestracja:prawie 7 lat
  • Ostatnio:prawie 7 lat
  • Postów:46
0

Mediator powinieneś używać, kiedy występuje sieć połączeń pomiędzy wieloma obiektami, które wywołują się na krzyż i tp. Po co ci on w twoim przypadku skoro i tak kontroler będzie wołał prawdopodobnie jeden lub dwa serwisy, no chyba że rzeczywiście każda klasa to inny handler dla innej komendy(SRP....heh), no bo zrobić jeden serwis spójny pojęciowo z normalnymi metodami to za łatwo. To i tak nie rozumiem po co ci mediator skoro ta komunikacja występuje po linii prostej Komenda-->Handler.? Przecież warstwę prezentacji piszesz pod konkretną logikę, zmieniasz logikę biznesową zmieniasz prezentację. Wyjaśnij mi co takiego może się zmienić w prezentacji co może w jakiś sposób przeszkadzać logice biznesowej? Jeśli nic to po co ci te pseudo odwracanie zależności? Jak i te pseudo proceduralne SRP.

Może jak nie ty, to ktoś inny mi to wyjaśni?

edytowany 1x, ostatnio: Enter Name
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:dzień
  • Lokalizacja:Wrocław
1
john_klamka napisał(a):

A co do ARów, nigdzie Evans nie mówi, że repozytorium może zwracać tylko ARy, natomiast wyraźnie mówi, że repozytorium realizuje dostęp do encji.

Nie wiem co mówi Evans, bo nigdy z nim nie gadałem, ale w swojej książce napisał:

A REPOSITORY represents all objects of a certain type as a conceptual set (usually simulated). It acts like a
collection, except with more elaborate querying capability. Objects of the appropriate type are added and
removed, and the machinery behind the REPOSITORY inserts them or deletes them from the database. This
definition gathers a cohesive set of responsibilities for providing access to the roots of AGGREGATES from
early-lifecycle through the end.

I dalej:

Only provide repositories for AGGREGATE
roots that actually need direct access. Keep the client focused on the model, delegating all object
storage and access to the REPOSITORIES.


neves napisał(a):

Mediator użyty do rozprowadzania komend może się przydać do implementacji jakiś cross cutting concerns, jak np logowania jakie komendy są wywoływane. Ale jeśli nie mamy żadnych ccc to równie dobrze można się objeść bez mediatora i bezpośrednio wywoływać command handlery z poziomu kontrolerów, które jakby nie były są zwyczajnymi serwisami aplikacyjnymi tylko z inną nazwą ;)

Tak, możemy się obejść - tak jak to robiliśmy w latach osiemdziesiątych. Tylko ja wtedy jeszcze nie programowałem, więc nie jestem jakimś wielkim fanem tamtego stylu, ani kultów cargo w ogólności. Przez osiem lat pisania w ten sposób udało mi się zebrać następujące spostrzeżenia:

  1. Jeśli mamy kontroler, który jest nakładką na jakiś application service i ma metody 1:1 wystawiające metody serwisu, to po prostu wygląda, jakby któraś z tych klas była tylko zbędnym, głupim wrapperem.
  2. Sztywne wiązanie utrudnia życie. Jeśli okaże się, że kontroler musimy korzystać z wielu serwisów, to trzeba dodawać nową referencję i nowe pole. Przeniesienie/skopiowanie jakiegoś ficzera do innego kontrolera - to samo. Im więcej zależności tym boleśniejsza refaktoryzacja.
  3. Czystość i higiena kodu - w małej klasie z jedną metodą nawet seniorowi trudno będzie złamać SRP. A przede wszystkim testy będą ładniejsze, bo gdy mamy god-serwisy, to praktycznie zawsze kończy się tak, że jeden test suit testuje wszystkie metody takiego serwisu. Efektem jest przeraźliwie długa klasa testowa ze skomplikowanym setupem. Klasy testujące handlery są krótsze i prostsze.
  4. Brudzi przy podejściu generycznym. Np. jeśli mamy generyczny serwis wystawiający dajmy na to wszystkie metody crudowe, a w kontrolerze nie potrzebujemy niektórych z nich.

Wad podejścia z mediatorem nie stwierdziłem, ale chętnie poznam choćby jedną.

edytowany 2x, ostatnio: somekind
EN
  • Rejestracja:prawie 7 lat
  • Ostatnio:prawie 7 lat
  • Postów:46
0

Jeśli mamy kontroler, który jest nakładką na jakiś application service i ma metody 1:1 wystawiające metody serwisu, to po prostu wygląda, jakby któraś z tych klas była tylko zbędnym, głupim wrapperem.

Ale kontroler ma być głupim wrapem, poza tym co mediator zmienia na tej płaszczyźnie.?

Sztywne wiązanie utrudnia życie. Jeśli okaże się, że kontroler musimy korzystać z wielu serwisów, to trzeba dodawać nową referencję i nowe pole. Przeniesienie/skopiowanie jakiegoś ficzera do innego kontrolera - to samo. Im więcej zależności tym boleśniejsza refaktoryzacja.

A to prawda te dwie rzeczy, są okropnie uciążliwe i zmniejszają czytelność kodu. Lepiej jest szukać w gąszczu klas nazwę pasującą do komendy.

Czystość i higiena kodu - w małej klasie z jedną metodą nawet seniorowi trudno będzie złamać SRP. A przede wszystkim testy będą ładniejsze, bo gdy mamy god-serwisy, to praktycznie zawsze kończy się tak, że jeden test suit testuje wszystkie metody takiego serwisu. Efektem jest przeraźliwie długa klasa testowa ze skomplikowanym setupem. Klasy testujące handlery są krótsze i prostsze.

To, że ktoś pisze testy pod klasę zamiast ficzer albo co gorsza jest seniorem. Nie ma nic wspólnego z dedykowanym handlerem, czy mediatorem.

Brudzi przy podejściu generycznym. Np. jeśli mamy generyczny serwis wystawiający dajmy na to wszystkie metody crudowe, a w kontrolerze nie potrzebujemy niektórych z nich.

No to wtedy ich nie używamy...?

Jak nie stwierdzasz, żadnych wad to może przedstawisz jakieś zalety?

edytowany 1x, ostatnio: Enter Name
EN
  • Rejestracja:prawie 7 lat
  • Ostatnio:prawie 7 lat
  • Postów:46
0

Im więcej zależności tym boleśniejsza refaktoryzacja.

O jakiej refaktoryzacji mówisz ? o refaktoryzacji metody w kontrolerze?, a to ciekawe. To nie wystarczy podmienić tylko metodę obiektu, który odbiera request?

Przecież i tak musisz mieć zależność do komendy i requestu więc co masz za chory problem, jeśli w tym samym namespace będzie serwis czy ten twój handler.

Bez sensu, Przecież mediator ma izolować komunikację a nie referencje, co ci ta referencja tam przeszkadza?.

EN
  • Rejestracja:prawie 7 lat
  • Ostatnio:prawie 7 lat
  • Postów:46
0

Minus jest taki, że ten mediator jest niepotrzebny i nie wnosi żadnej wartości do projektu. No, chyba że jesteś uparty na dedykowane handlery. Ponieważ przewidujesz późniejsze skalowanie tych handlerów ale po co w ten sposób zaczynać pisać aplikację?. Nie wiem.

john_klamka
  • Rejestracja:prawie 9 lat
  • Ostatnio:prawie 5 lat
  • Postów:177
1
somekind napisał(a):

Nie wiem co mówi Evans, bo nigdy z nim nie gadałem (...)

Za słownikiem języka polskiego PWN:
mówić:

  1. «posługiwać się słowami w celu komunikowania myśli i przeżyć lub przekazywania informacji»
  2. «posługiwać się jakimś językiem»
  3. «zwracać się do kogoś w jakiejś formie»
  4. «informować, podawać do wiadomości, zawierać jakąś treść»
  5. «wypowiadać jakiś tekst, zwykle na głos»
  6. «wyrażać coś, coś znaczyć, świadczyć o czymś»

Czepianie się słówek sprowadza dyskusję do parteru, nie rób tego, proszę.

A REPOSITORY represents all objects of a certain type as a conceptual set (usually simulated). It acts like a
collection, except with more elaborate querying capability. Objects of the appropriate type are added and
removed, and the machinery behind the REPOSITORY inserts them or deletes them from the database. This
definition gathers a cohesive set of responsibilities for providing access to the roots of AGGREGATES from
early-lifecycle through the end.

I dalej:

Only provide repositories for AGGREGATE
roots that actually need direct access. Keep the client focused on the model, delegating all object
storage and access to the REPOSITORIES.

Oczywiście w idealnym świecie tak jest, natomiast w rzeczywistych projektach nie da się zrealizować wszystkich wytycznych Evansa - np. z powodu ograniczeń technologicznych. Zresztą sam wiele razy wspominał o tym, że DDD przede wszystkim ma służyć zaprojektowaniu systemu, który przynosi wartość dla klienta.

W niebieskiej książce jest poza tym napisane:
"Even a REPOSITORY design with flexible queries should allow for the addition of specialized hardcoded
queries. They might be convenience methods that encapsulate an often-used query or a
query that doesn't return the objects themselves, such as a mathematical summary of selected
objects."
i chociaż tu jest mowa tylko o metodach w repozytorium, to sam widzisz, że Evans dopuszcza pewne kompromisy w systemie.

A tak w ramach ciekawostki, spotkałem się z repozytorium zwracającym DTO mapujące pewien AR do użycia w w http api z pominięciem serwisu aplikacji - i w kontekście tego konkretnego projektu miało to sens.

Jeśli zaś mówimy o repozytorium oderwanym całkowicie od pozostałych wzorców taktycznych, czyli typowym "entity framework repository pattern tutorial", to zgadzam się z Tobą w pełni i uważam, że powinno się za to palić na stosie ;)

1

Programuje od prawie dwóch lat i staram się łapać tematy jak wzorce, DDD, CQRS czy inne. Głównie siedzę w WebApi (tak .NET). I... Czytając ten wątek wywracane jest praktycznie wszystko co "uczą" w książkach (przynajmniej tych podstawowych).

somekind napisał(a):
Jeśli mamy kontroler, który jest nakładką na jakiś application service i ma metody 1:1 wystawiające metody serwisu, to po prostu wygląda, jakby któraś z tych klas była tylko zbędnym, głupim wrapperem.

Dokładnie tak jest! Moje metody WebApi w większości odzwierciedlają metody z serwisów. Czasami metoda kontrolera wywoła dwie z serwisów.

Kopiuj
[Route("api/users")]
IHttpActionResult Get() => Ok(this._usersDao.GetAll());

Tak to wygląda.

Jakiś czas temu wprowadziłem mediatora i jedna metoda WebApi wysyłała jedną komendę lub query do mediatora.
Koniec końców wciąż było tak, że metody kontrolera tylko przekazywały coś niżej.
Rozumiem, że największy plus tego to fakt, iż kontrolery WebApi nie wiedziały nic o Dao?

Kopiuj
[Route("api/users")]
IHttpActionResult Get(GetAllUsersQuery query) => Ok(this.mediator.Execute(query)); // I to jest w porządku?
Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
0
Nadziany Szczur napisał(a):

Jakiś czas temu wprowadziłem mediatora i jedna metoda WebApi wysyłała jedną komendę lub query do mediatora.
Koniec końców wciąż było tak, że metody kontrolera tylko przekazywały coś niżej.
Rozumiem, że największy plus tego to fakt, iż kontrolery WebApi nie wiedziały nic o Dao?

Tak, poza tym kod staje się wtedy bardziej ekspresywny. Kontroler nie wie jak coś jest wykonane, wysyła tylko polecenie i zwraca wynik. Poza tym tak jak było wspomniane- kontrolera nie zasmieca się serwisami, a handler dostaje tylko te serwisy które są potrzebne do wykonania konkretnej operacji.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
EN
  • Rejestracja:prawie 7 lat
  • Ostatnio:prawie 7 lat
  • Postów:46
0
Nadziany Szczur napisał(a):

Programuje od prawie dwóch lat i staram się łapać tematy jak wzorce, DDD, CQRS czy inne. Głównie siedzę w WebApi (tak .NET). I... Czytając ten wątek wywracane jest praktycznie wszystko co "uczą" w książkach (przynajmniej tych podstawowych).

somekind napisał(a):
Jeśli mamy kontroler, który jest nakładką na jakiś application service i ma metody 1:1 wystawiające metody serwisu, to po prostu wygląda, jakby któraś z tych klas była tylko zbędnym, głupim wrapperem.

Dokładnie tak jest! Moje metody WebApi w większości odzwierciedlają metody z serwisów. Czasami metoda kontrolera wywoła dwie z serwisów.

Kopiuj
[Route("api/users")]
IHttpActionResult Get() => Ok(this._usersDao.GetAll());

Tak to wygląda.

Jakiś czas temu wprowadziłem mediatora i jedna metoda WebApi wysyłała jedną komendę lub query do mediatora.
Koniec końców wciąż było tak, że metody kontrolera tylko przekazywały coś niżej.
Rozumiem, że największy plus tego to fakt, iż kontrolery WebApi nie wiedziały nic o Dao?

Kopiuj
[Route("api/users")]
IHttpActionResult Get(GetAllUsersQuery query) => Ok(this.mediator.Execute(query)); // I to jest w porządku?

Bo tak mówi Scott Allen, który, również promuje, crudowe generyczne repository z EF. Oraz jakieś chore kombinacje typu repository w Unit Of Work. Którego fanem jest Aventus

Ale co ty tam wiesz. Nawet jak możesz wstrzyknąć jeden serwis to masz używać mediatora, bo bogowie tego forum mówią, że tak ma być i koniec. Nie wypowiadaj się, bo cię zbanują albo wyśmieją.

Czepianie się słówek sprowadza dyskusję do parteru, nie rób tego, proszę.

Ja już jakoś się tym nie przejmuje.

Aventus napisał(a):

Tak, poza tym kod staje się wtedy bardziej ekspresywny. Kontroler nie wie jak coś jest wykonane, wysyła tylko polecenie i zwraca wynik. Poza tym tak jak było wspomniane- kontrolera nie zasmieca się serwisami, a handler dostaje tylko te serwisy które są potrzebne do wykonania konkretnej operacji.

To ile jest tych serwisów w handlarze, 3 - 5 a może 10 ? Daruj sobie, szkoda tego słuchać.... Lepiej pójść pokopać piłkę albo pozjeżdżać na poręczy od klatki schodowej.

Tak, poza tym kod staje się wtedy bardziej ekspresywny. Kontroler nie wie jak coś jest wykonane, wysyła tylko polecenie i zwraca wynik

Dokładnie to samo uzyskujesz bez mediatora z serwisem. No i...?

edytowany 3x, ostatnio: Shalom
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:dzień
  • Lokalizacja:Wrocław
0
john_klamka napisał(a):

Czepianie się słówek sprowadza dyskusję do parteru, nie rób tego, proszę.

Wiesz, może to był suchar, ale bez przesady. Można chyba sobie trochę pożartować nawet dyskutując na poważne tematy. ;)

Oczywiście w idealnym świecie tak jest, natomiast w rzeczywistych projektach nie da się zrealizować wszystkich wytycznych Evansa - np. z powodu ograniczeń technologicznych. Zresztą sam wiele razy wspominał o tym, że DDD przede wszystkim ma służyć zaprojektowaniu systemu, który przynosi wartość dla klienta.

Ja po prostu jestem za tym, żeby który nie jest robiony z duchem DDD nie stosował tamtejszej nomenklatury. Udawanie w ten sposób profesjonalizmu sprawa jedynie, że wychodzi się na ignoranta i laika.

W niebieskiej książce jest poza tym napisane:
"Even a REPOSITORY design with flexible queries should allow for the addition of specialized hardcoded
queries. They might be convenience methods that encapsulate an often-used query or a
query that doesn't return the objects themselves, such as a mathematical summary of selected
objects."
i chociaż tu jest mowa tylko o metodach w repozytorium, to sam widzisz, że Evans dopuszcza pewne kompromisy w systemie.

Ale gdzie tu kompromis? Tu jest mowa o dwóch podejściach do implementacji w zależności od potrzeb.

A tak w ramach ciekawostki, spotkałem się z repozytorium zwracającym DTO mapujące pewien AR do użycia w w http api z pominięciem serwisu aplikacji - i w kontekście tego konkretnego projektu miało to sens.
Jeśli zaś mówimy o repozytorium oderwanym całkowicie od pozostałych wzorców taktycznych, czyli typowym "entity framework repository pattern tutorial", to zgadzam się z Tobą w pełni i uważam, że powinno się za to palić na stosie ;)

No, a czym takie "repozytorium" z tutoriala rózni się od "repozytorium" zwracającego DTO dla API? Dodanie sufiksu "repository" do nazwy klasy nie czyni z niej repozotorium, i tak jest w obu tych sytuacjach.
Można sobie wstawić kuchenkę gazową oraz lodówkę do łazienki i nazwać takie pomieszczenie "kuchnią", ale to nie zmieni faktu, że tam nadal jest wanna, kibel i jest to łazienka.

Nadziany Szczur napisał(a):

Programuje od prawie dwóch lat i staram się łapać tematy jak wzorce, DDD, CQRS czy inne. Głównie siedzę w WebApi (tak .NET). I... Czytając ten wątek wywracane jest praktycznie wszystko co "uczą" w książkach (przynajmniej tych podstawowych).

Jeśli masz na myśli książki np. Freemana odnoście programowania MVC, z repozytoriami wstrzykiwanymi do kontrolerów, które to realizują logikę biznesową to owszem, tam jest pokazywane niepoprawne podejście - w rzeczywistości ani to MVC, ani to repozytoria.

EN
  • Rejestracja:prawie 7 lat
  • Ostatnio:prawie 7 lat
  • Postów:46
0

Jeśli masz na myśli książki np. Freemana odnoście programowania MVC, z repozytoriami wstrzykiwanymi do kontrolerów, które to realizują logikę biznesową to owszem, tam jest pokazywane niepoprawne podejście - w rzeczywistości ani to MVC, ani to repozytoria.

Mnie się wydaje, że mu chodzi o mediatora oraz proceduralny styl programowania, gdzie obiekt równa się procedura. Którego, jesteś fanem.

SO
  • Rejestracja:ponad 10 lat
  • Ostatnio:12 miesięcy
1

Tak się podepnę pod temat - a co myślicie o używaniu QueryObjectów, bo też widziałem, że niektórzy pokazują implementacje przy użyciu interfejsów w stylu IQuery<TResult>, IQueryHandler<TQuery>, a inni że to be i QueryObjecty niepotrzebne.

Żeby nie tworzyć bezsensownych wrapperów na ORMowe repo myślałem, żeby zrobić generycznego CRUDa, który pod spodem korzysta bezpośrednio np. z DbSeta, a jak potrzebuję bardziej specyficzne query to zrobię sobie query objecty.
Wydawało mi się to w miarę ok podejście, bo:

  1. nie mam bezsensownych wrapperów na ORMa, które w dłuższej mierze powodują tylko problemy np. wystawianie IQueryable, czy Specification z takiego repo,
  2. jak w kilku miejscach potrzebuję zrobić to samo query to po prostu używam tego samego query objecta (widziałem projekty, gdzie generyczne repo zwracało IQueryable, a jak później w kilku miejscach trzeba było zrobić takie samo query to od nowa było składane wyrażenie LINQ na 15 linii kodu z zagnieżdżonymi Selectami :|),
  3. 1 klasa = 1 query wydawało mi się lepszym podejściem, niż repo z 10 metodami (GetXXXWithYYYWithoutZZZHavingAAA)

Ale jak zwykle(jak chyba przy każdym wzorcu) na 5 opinii każda jest inna :D

neves
  • Rejestracja:ponad 21 lat
  • Ostatnio:2 dni
  • Lokalizacja:Kraków
  • Postów:1114
1

Taaaaa tu się to ostatnio pojawiło https://geek.justjoin.it/command-query-responsibility-segregation-pierwsze-kroki/, i jeszcze to przez mediatora przepuszczają tak jak komendy :D. Mój stosunek jest taki sam jak do komend, jeśli potrzebujemy jakieś programowanie aspektowe/cross cutting concerns w okół przetwarzania komend/zapytań to można to przepuszczać przez mediatora. Jeśli nie mamy żadnego aop/ccc to nie ma potrzeby używania mediatora.

Natomiast zawsze jest dobrze mieć ten kod oddzielony od infrasturktury/kontrolerów, bez różnicy czy się to nazwie i opakuje QueryHandlerem, DAO, Repository czy jakimś Servicem.


Zobacz pozostałe 20 komentarzy
EN
No ale pobieranie danych nie ma żadnych reguł biznesowych. Co to ma wspólnego z domeną w ogóle, no, chyba że to zaawansowana wyszukiwarka. Domena to jakieś reguły typu, "jeśli założysz konto w poniedziałek dostaniesz rabat 20%." Sam TS powinien zajmować się transakcjami niepobieraniem danych.
EN
Poza tym moduł to pojęcie abstrakcyjne zwykle odnosi się do namespace nie zależnie od warstw.
hcubyc
Dla mnie moduł to coś co można przenieść między dowolną aplikacją, nie zależy od infrastruktury, zawiera całą domenę. Tak, pobieranie danych nie posiada reguł biznesowych, ale ideowo operuje na tych samych pojęciach i jeżeli chcesz przenieść moduł, np wydzielić mikroserwis z istniejącej aplikacji to potrzebujesz też części do pobierania danych.
EN
Ale to zależy od modułu. Moduł może też zawierać swoją infrastrukturę, utlisy, itp. np. Domani.module1, Utils.module1, DataReader.module1.
EN
Neves pewnie się cieszy, że dostał tyle komentarzy ;)
EN
  • Rejestracja:prawie 7 lat
  • Ostatnio:prawie 7 lat
  • Postów:46
0
neves napisał(a):

Taaaaa tu się to ostatnio pojawiło https://geek.justjoin.it/command-query-responsibility-segregation-pierwsze-kroki/, i jeszcze to przez mediatora przepuszczają tak jak komendy :D. Mój stosunek jest taki sam jak do komend, jeśli potrzebujemy jakieś programowanie aspektowe/cross cutting concerns w okół przetwarzania komend/zapytań to można to przepuszczać przez mediatora. Jeśli nie mamy żadnego aop/ccc to nie ma potrzeby używania mediatora.

Natomiast zawsze jest dobrze mieć ten kod oddzielony od infrasturktury/kontrolerów, bez różnicy czy się to nazwie i opakuje QueryHandlerem, DAO, Repository czy jakimś Servicem.

Nie musisz, wystarczy, że w kontenerze IOC zaimplementujesz sobie interoceptor albo dekorator, jeśli to ci bardziej pasuje. Kontener w tym kontekście jest o wiele bardziej elastyczny.

Mój stosunek jest taki, że mediator ma pełnić funkcje fasady z naciskiem na izolowanie tego, jak coś się komunikuje.

I nie jest żadną dobrą praktyki w stylu "Controller on diet with mediator".
A praktyki, typu rozdzielanie obiektów na:

Kopiuj
new CreateproductHandlerComand.Handle(command)
new ChangeProductPriceHandlerComand.Handle(command)

Zamiast

Kopiuj
new Product(Id, Price);
Product.ChangePrice(command);

// or

new ProductSerwice();
Product.CreateProduct(command);
Product.ChangePrice(command);

Jako że dwa obiekty są zgodne z SRP, A jeden nie.
Jest błędem w rozumowaniu na poziomie logiki.

Jak i podobne tego typu bzdety jak łatwiejsze testowanie, lepsza struktura projektu (Chociaż sam autor bloszka trzyma komendy w domenie, a to już jest znak, że coś się dzieje....) i tp.

edytowany 5x, ostatnio: Enter Name
neves
to ja już wolę mediator od używania interoceptorów, mniejsza magia
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)