Moduł jest logicznym zgrupowaniem elementów (ściśle powiązanych ze sobą funkcjonalności) odpowiadających za konkretną rzecz – na przykład autoryzację.
Podstawowe zasady
Moduł powinien być jak najbardziej autonomiczny biznesowo, czyli musi spełniać zasadę SRP. Moduły mogą być w sobie zagnieżdżone (jeden moduł może zawierać w sobie inne moduły). Każdy moduł powinien być unikalny względem naszej aplikacji oraz jak najbardziej niezależny od innych modułów. Powinien również wystawiać API za pomocą którego będzie odbywać się komunikacja z innymi modułami. Przy podziale aplikacji na moduły powinniśmy dbać o niski coupling i wysoką kohezję.

Zalety wynikające z podziału aplikacji na moduły
Łatwiejsze zrozumienie, utrzymanie oraz rozwój aplikacji
Każdy moduł stanowi autonomiczną jednostkę, w której możemy dokonywać zmian bez obawy, że wpłyną one na cały system. Szczególnie jeśli implementację schowamy za warstwą abstrakcji, która jest bardziej stabilna (rzadziej się zmienia). Oczywiście jeśli zmienimy publiczne API modułu to zmiany będą oddziaływać również na moduły, które się z nim komunikują. Jest to jednak bardzo rzadki przypadek. Co więcej osoba dokonująca zmian nie musi analizować całego systemu, a jedynie moduł który chce zmienić. Czasami w wyniku zmian możemy dojść do momentu w którym nie będziemy już potrzebować jakiegoś modułu, ponieważ wymagania funkcjonalne się zmienią. W takiej sytuacji dzięki temu, że moduły są autonomiczne w bardzo łatwy sposób możemy usunąć zbędny kod.
Reużywalność oraz wymienialność
Dzięki temu, że moduły są autonomiczne można je ponownie wykorzystać w innych aplikacjach co znacząco przyspieszy czas realizacji oraz zmniejszy koszta. Dodatkowo jeśli uznamy, że dany moduł nie spełnia naszych założeń możemy go w łatwy sposób podmienić. Wystarczy, że podstawimy za niego moduł, który wystawia takie samo API.
Poprawa testowalności
Autonomiczność modułów wpływa również pozytywnie na testowalność. Możemy je testować niejako w izolacji (do testów nie potrzebujemy innych modułów po za modułem, który testujemy). Dzięki temu testy mogą się nie powieść tylko z jednego powodu – kiedy coś nie działa w module, który testujemy. Testy są również o wiele łatwiejsze do napisania, ponieważ nie jesteśmy zmuszeni do pisania dużej ilości mocków (mockujemy tylko zewnętrzne moduły). Testy są również krótsze oraz czytelniejsze, ponieważ testujemy tylko jeden moduł, a nie całą aplikację. Dodatkowo dzięki temu, że nasze testy będą bazować na abstrakcji możemy podmieniać szczegóły implementacyjne modułu bez konieczności zmiany testów, które nadal powinny przechodzić.