-
Notifications
You must be signed in to change notification settings - Fork 35
feat(squads): bounded contexts + ADRs for onboarding & squads governance #332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
danielhe4rt
wants to merge
11
commits into
4.x
Choose a base branch
from
feat/onboarding-squads
base: 4.x
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 10 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
7af50a0
chore(squads): scaffold module structure
danielhe4rt 30712e2
chore(onboarding): scaffold module structure
danielhe4rt 1d669b6
chore(deps): register onboarding and squads modules
danielhe4rt c9e3168
docs(onboarding): add bounded-context glossary
danielhe4rt e784d19
docs(onboarding): add ADR-0001 polymorphic onboarding by type
danielhe4rt 33fbfa4
docs(onboarding): add ADR-0002 PR-approval signal via github domain e…
danielhe4rt e696486
docs(squads): add bounded-context glossary
danielhe4rt 0cb4fc3
docs(squads): add ADR-0001 governance as record-keeping
danielhe4rt 9114069
docs(squads): add ADR-0002 squads data model
danielhe4rt dd2a5d5
docs(architecture): map onboarding and squads contexts
danielhe4rt 36262a8
docs(squads): apply new questioning
danielhe4rt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| # Onboarding Context | ||
|
|
||
| The universal, mandatory entry layer of the ecosystem. Owns the polymorphic onboarding state | ||
| machines that a person walks through, and the per-type **completion** status that other contexts | ||
| consume as an access gate. Today it powers community entry (`Welcome`) and squad entry (`Squads`), | ||
| but it is designed so new onboarding types are added without touching consumers. | ||
|
|
||
| ## Glossary | ||
|
|
||
| | Term | Definition | Not to be confused with | | ||
| | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | ||
| | **Onboarding** | One person's journey through one typed flow, scoped to a tenant. One row per `(tenant, user, type)`. Holds lifecycle `status`, not the per-step detail. | The UI wizard (presentation) — the Onboarding is the persisted state machine | | ||
| | **OnboardingType** | The discriminator enum (`Welcome`, `Squads`, …). Resolves the polymorphic behaviour via `handler(): OnboardingFlow` — same idiom as `IdentityProvider::getClient`. | A step — a type _has_ steps | | ||
|
danielhe4rt marked this conversation as resolved.
|
||
| | **OnboardingFlow** | The per-type handler (a class behind the `OnboardingFlow` contract). Declares `steps()`, `prerequisites()`, `advance()`, `isComplete()`. All type-specific rules live here. | The model — the Flow is stateless behaviour; the Onboarding is the state | | ||
| | **OnboardingStep** | One auditable stage of a flow, one row in `onboarding_steps`. Carries its own `status` + `data` (jsonb) + `completed_at`. Enables pause/resume and history. | A prerequisite (another _type_ that must be complete first) | | ||
| | **Prerequisite** | Another `OnboardingType` that must be **completed** before this one can start (e.g. `Squads` requires `Welcome`). Declared by the flow, enforced on start. | A step (intra-type) — a prerequisite is inter-type | | ||
| | **APTO** | Domain shorthand for "completed the `Squads` onboarding". The condition that unlocks squad candidacy and squad creation. It is _not_ a global flag — it is `Squads` completion. | A generic "active member" — APTO is specifically squad-eligible | | ||
| | **Challenge** | The Git step of the `Squads` flow: open a PR (with the mandatory template) on a `challenge` repo; a human reviewer approves it on GitHub. Curation happens entirely on GitHub. | A contribution (the gamification record) — challenge repos do **not** award XP | | ||
| | **Gate** | A pre-condition checked at a transition without being a step of its own. The `Squads` flow gates `git_challenge` on having a linked GitHub `ExternalIdentity`. | A step — a gate produces no `onboarding_steps` row | | ||
|
|
||
| ## State machine (shape) | ||
|
|
||
| `status` is a generic lifecycle: `in_progress` · `paused` · `completed` · `rejected`. The _steps_ are | ||
| type-specific and defined by the flow. The `Squads` flow: | ||
|
|
||
| ``` | ||
| [prereq: Welcome completed?] | ||
| │ yes | ||
| ▼ | ||
| step: form ──(submit, auto-advance, no curation)──► [gate: GitHub linked?] | ||
| │ no -> blocked + CTA "link GitHub" | ||
| │ yes | ||
| ▼ | ||
| step: git_challenge ──(PR approved on challenge repo)──► completed = APTO | ||
| ``` | ||
|
|
||
| Pause/resume is orthogonal: `status = paused` + `paused_at`, resumable from the current step. | ||
|
|
||
| ## Structure (proposed) | ||
|
|
||
| ``` | ||
| src/ | ||
| ├── Models/ ← Onboarding · OnboardingStep | ||
| ├── Enums/ ← OnboardingType · OnboardingStatus · OnboardingStepStatus | ||
| ├── Contracts/ ← OnboardingFlow | ||
| ├── Flows/ ← WelcomeOnboardingFlow · SquadsOnboardingFlow | ||
| ├── Actions/ ← StartOnboarding · AdvanceStep · PauseOnboarding · ResumeOnboarding | ||
| ├── DTOs/ ← per-step payload contracts (validated by the flow) | ||
| └── Listeners/ ← GithubPullRequestApproved -> advance the challenge step | ||
| ``` | ||
|
|
||
| ## Module Boundaries | ||
|
|
||
| ### This module owns: | ||
|
|
||
| - The onboarding state machines (one polymorphic model + steps) and their lifecycle. | ||
| - The per-type **completion** status other modules read as a gate (`Onboarding::isCompleted(user, tenant, type)`). | ||
| - The inter-type prerequisite chain. | ||
|
|
||
| ### This module does NOT own: | ||
|
|
||
| - Squad lifecycle, membership, governance — belongs to `squads` (a consumer of the gate). | ||
| - Any HTTP communication with GitHub or the raw event lake — belongs to `integration-github`. This | ||
| module **listens to** `GithubPullRequestApproved` and reads `GithubRepository` (`purpose = challenge`). | ||
| - GitHub account linking — belongs to `identity` (`ExternalIdentity`, provider `github`). | ||
|
|
||
| ## Dependencies | ||
|
|
||
| - **Identity** — `User`, tenant scoping, and the GitHub `ExternalIdentity` link (the `git_challenge` gate). | ||
| - **Integration GitHub** — consumes the `GithubPullRequestApproved` domain event and the `challenge` | ||
| repo allowlist. Never the reverse. | ||
| - Presentation (`panel-app`) drives the UI and calls the module's Actions. | ||
|
|
||
| See `docs/adr/0001-onboarding-polimorfico-por-tipo.md` and | ||
| `docs/adr/0002-sinal-de-pr-aprovado-via-evento-de-dominio.md`. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| { | ||
| "name": "he4rt/onboarding", | ||
| "description": "", | ||
| "type": "library", | ||
| "version": "1.0.0", | ||
| "license": "proprietary", | ||
| "autoload": { | ||
| "psr-4": { | ||
| "He4rt\\Onboarding\\": "src/", | ||
| "He4rt\\Onboarding\\Database\\Factories\\": "database/factories/", | ||
| "He4rt\\Onboarding\\Database\\Seeders\\": "database/seeders/" | ||
| } | ||
| }, | ||
| "autoload-dev": { | ||
| "psr-4": { | ||
| "He4rt\\Onboarding\\Tests\\": "tests/" | ||
| } | ||
| }, | ||
| "minimum-stability": "stable", | ||
| "extra": { | ||
| "laravel": { | ||
| "providers": [ | ||
| "He4rt\\Onboarding\\Providers\\OnboardingServiceProvider" | ||
| ] | ||
| } | ||
| } | ||
| } |
Empty file.
Empty file.
Empty file.
110 changes: 110 additions & 0 deletions
110
app-modules/onboarding/docs/adr/0001-onboarding-polimorfico-por-tipo.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| # ADR-0001: Onboarding polimórfico por tipo, com etapas auditáveis | ||
|
|
||
| **Status:** Accepted | ||
| **Date:** 2026-06-15 | ||
| **Deciders:** danielhe4rt | ||
|
|
||
| ## Contexto | ||
|
|
||
| Os Squads da He4rt rodam hoje no informal (grupos de WhatsApp, sem liderança formal). A primeira | ||
| entrega de software ataca **governança** (capitão/subcapitão, eleição, etc.) e, antes dela, uma | ||
| camada de **Entrada** que filtra quem realmente quer integrar a comunidade e contribuir. | ||
|
|
||
| Essa Entrada — chamada no documento da P.O. de "pré-triagem" — é **universal e obrigatória**: | ||
| ninguém se candidata a um squad nem propõe um squad novo sem concluí-la. Duas perguntas de fronteira | ||
| apareceram: | ||
|
|
||
| 1. **Onde mora?** A pré-triagem é sobre pertencer à comunidade, não a um squad específico, e reusa | ||
| pesado o `identity` (vínculo GitHub via `ExternalIdentity`). Colocá-la dentro de `squads` | ||
| amarraria um conceito universal a um consumidor específico. | ||
|
danielhe4rt marked this conversation as resolved.
Outdated
|
||
| 2. **Quantos formatos?** O time já enxerga **mais de um tipo de entrada** — `Welcome` (entrada na | ||
| comunidade) e `Squads` (entrada no programa) — e quer outros no futuro, cada um com seu próprio | ||
| contrato de payload e processamento. | ||
|
|
||
| Modelar a pré-triagem como uma máquina de estados fixa (form → desafio) resolveria o `Squads` de | ||
| hoje, mas não comportaria novos tipos sem refator. | ||
|
|
||
| ## Decisão | ||
|
|
||
| **Criar um módulo de domínio novo, `onboarding`, dono de máquinas de onboarding polimórficas por | ||
| tipo.** `squads` (e futuros consumidores) apenas leem o gate de conclusão. | ||
|
|
||
| ### Polimorfismo (enum → handler) | ||
|
|
||
| - `OnboardingType` (enum) discrimina o tipo e resolve o comportamento via `handler(): OnboardingFlow` | ||
| — mesmo idioma que `IdentityProvider::getClient()` já usa no `identity`. | ||
| - `OnboardingFlow` (contrato) declara `steps()`, `prerequisites()`, `advance()`, `isComplete()`. | ||
| Toda regra específica do tipo vive no handler; nenhum consumidor conhece os tipos concretos. | ||
|
|
||
| ### Persistência (modelo + etapas) | ||
|
|
||
| Modelo único discriminado por `type` + tabela de etapas (opção "C" avaliada): | ||
|
|
||
| `onboardings` — uma linha por `(tenant_id, user_id, type)`: | ||
|
|
||
| | Coluna | Tipo | Notas | | ||
| | -------------- | ---------------------------- | --------------------------------------------------------------------- | | ||
| | `id` | uuid (PK) | `HasUuids` | | ||
| | `tenant_id` | uuid (FK) | tenant-scoped (convenção do repo, multi-tenant-ready) | | ||
| | `user_id` | uuid (FK) | | | ||
| | `type` | string | `OnboardingType` (`welcome` \| `squads` \| …) | | ||
| | `status` | string | ciclo de vida genérico: `in_progress`/`paused`/`completed`/`rejected` | | ||
| | `completed_at` | timestamptz? | | | ||
| | `paused_at` | timestamptz? | | | ||
| | timestamps | tz | | | ||
| | UNIQUE | `(tenant_id, user_id, type)` | | | ||
|
|
||
| `onboarding_steps` — uma linha por etapa do fluxo: | ||
|
|
||
| | Coluna | Tipo | Notas | | ||
| | --------------- | --------------------------- | ------------------------------------------------- | | ||
| | `id` | uuid (PK) | | | ||
| | `onboarding_id` | uuid (FK) | | | ||
| | `step_key` | string | semântica do handler (`form`, `git_challenge`, …) | | ||
| | `status` | string | `pending`/`done`/… | | ||
| | `data` | jsonb | payload da etapa, validado pelo DTO do tipo | | ||
| | `completed_at` | timestamptz? | | | ||
| | timestamps | tz | | | ||
| | UNIQUE | `(onboarding_id, step_key)` | | | ||
|
|
||
| A tabela de etapas (em vez de só um `payload` JSON no modelo) foi escolhida por dar **auditoria e | ||
| histórico por etapa de graça** — relevante pro desafio Git, que tem reenvio com evolução e | ||
| pausa/retoma. | ||
|
|
||
| ### Cadeia entre tipos | ||
|
|
||
| `prerequisites()` declara dependências **inter-tipo**: `Squads` exige `Welcome` concluído para poder | ||
| iniciar. O gate que o `squads` consome é `Onboarding::isCompleted(user, tenant, Squads)` — apelidado | ||
| de **APTO** no domínio. | ||
|
|
||
| ## Alternativas consideradas | ||
|
|
||
| - **Pré-triagem dentro de `identity`** (membership): conceitualmente limpo, mas mistura onboarding | ||
| evolutivo com o núcleo de autenticação e força `identity` a depender de `integration-github`. | ||
| - **Pré-triagem dentro de `squads`**: entrega rápida, mas amarra um conceito universal a um consumidor | ||
| e exigiria migração quando outro módulo (eventos, etc.) quiser o mesmo gate. | ||
| - **STI / modelo por tipo**: Laravel não tem STI nativo (exige pacote/boilerplate), foge do idioma | ||
| `enum→resolve` do repo e incha o schema com colunas nuláveis por tipo. Descartado. | ||
|
danielhe4rt marked this conversation as resolved.
|
||
| - **Modelo único só com `payload` JSON (sem tabela de etapas)**: mais simples, mas perde auditoria | ||
| por etapa. É o passo anterior natural; promovido para a tabela de etapas por causa do desafio Git. | ||
|
|
||
| ## Consequências | ||
|
|
||
| ### Positivas | ||
|
|
||
| - Somar um tipo novo = +1 case no enum + 1 classe `Flow`. Consumidores intactos. | ||
| - Auditoria etapa-a-etapa nativa (início/fim de cada etapa, tentativas de reenvio). | ||
| - Pausa/retoma natural (estado vive na etapa + `status=paused`). | ||
| - Núcleo do jogo (futuro) pode virar mais um tipo, ou consumir o gate, sem acoplar. | ||
|
|
||
| ### Negativas / diferidas | ||
|
|
||
| - `status`/`step_key` são strings genéricas — a disciplina de transição fica no handler, não no banco. | ||
| - Dois lugares de verdade (`onboardings` + `onboarding_steps`); o `isComplete()` precisa ser a única | ||
| fonte que decide conclusão para não divergirem. | ||
| - Validação do payload é só de aplicação (DTO), não de schema. | ||
|
|
||
| ## Review trigger | ||
|
|
||
| Revisitar quando (a) o Núcleo do jogo for refinado e a gente decidir se ele é um `OnboardingType` ou | ||
| um consumidor do gate; ou (b) surgir um tipo cujo estado de etapa não caiba no par modelo+steps. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.