✨ Your new, shiny Nx workspace is almost ready ✨.
Learn more about this workspace setup and its capabilities or run npx nx graph to visually explore what was created. Now, let's get you up to speed!
Click here to finish setting up your workspace!
To run the dev server for your app, use:
npx nx serve mobileTo create a production bundle:
npx nx build mobileTo see all available targets to run for a project, run:
npx nx show project mobileThese targets are either inferred automatically or defined in the project.json or package.json files.
More about running tasks in the docs »
While you could add new projects to your workspace manually, you might want to leverage Nx plugins and their code generation feature.
Use the plugin's generator to create new projects.
To generate a new application, use:
npx nx g @nx/expo:app demoTo generate a new library, use:
npx nx g @nx/react:lib mylibYou can use npx nx list to get a list of installed plugins. Then, run npx nx list <plugin-name> to learn about more specific capabilities of a particular plugin. Alternatively, install Nx Console to browse plugins and generators in your IDE.
Learn more about Nx plugins » | Browse the plugin registry »
Nx Console is an editor extension that enriches your developer experience. It lets you run tasks, generate code, and improves code autocompletion in your IDE. It is available for VSCode and IntelliJ.
Learn more:
- Learn more about this workspace setup
- Learn about Nx on CI
- Releasing Packages with Nx release
- What are Nx plugins?
And join the Nx community:
Estamos desenvolvendo uma aplicação que simula um inventário de RPG, inspirado na experiência de jogos como Skyrim, com foco em boas práticas de arquitetura (DDD + CQRS), autenticação moderna, React Native, React Web e um backend escalável com NestJS + TRPC.
O sistema terá duas interfaces principais:
- Um painel web administrativo (Admin) para cadastro e gerenciamento de itens.
- Um aplicativo mobile (Expo + React Native) para que os usuários acessem, filtrem e manipulem seu inventário.
O backend deve ser implementado em NestJS seguindo os princípios do DDD (Domain-Driven Design) e utilizando o padrão arquitetural CQRS (Command Query Responsibility Segregation) por meio da biblioteca @nestjs/cqrs.
A aplicação deve ser dividida de forma clara entre:
- Camada de domínio: entidades, agregados, repositórios, eventos de domínio.
- Camada de aplicação: casos de uso, comandos, queries, handlers, DTOs.
- Camada de infraestrutura: TRPC, bancos de dados, autenticação, providers externos, integração com event bus.
- Camada de interface: endpoints TRPC, controladores, mapeamentos.
O uso de CQRS permite separar comandos (ações que alteram o estado) de queries (leitura de dados), promovendo clareza, escalabilidade e testes mais previsíveis.
A comunicação assíncrona entre partes do sistema (como envio de e-mails, recuperação de senha, eventos de domínio, etc.) deve ser feita por meio de um EventBus.
- Utilizar o Inngest como implementação inicial do EventBus.
- Utilizar a lib
nest-inngest. - O EventBus deve ser abstraído por uma interface, permitindo substituição futura por outro provedor (ex: SQS, Kafka, Redis Streams etc.).
- Essa arquitetura garante baixo acoplamento e alta escalabilidade.
O EventBus deve ser implementado de forma desacoplada, e estar localizado na infraestrutura, mas ser consumido diretamente nos casos de uso da camada de aplicação ou por eventos de domínio disparados por agregados da camada de domínio.
Integração completa com o BetterAuth, incluindo:
- Login por e-mail e senha e Google OAuth.
- Implementar um provider de autenticação para o NestJS usando BetterAuth (não existe pronto — será construído do zero).
- Integração com BetterAuth Expo no aplicativo mobile.
- Integração com React Query no frontend.
Utilizando:
- Inngest para gerenciar a fila de recuperação de senha e envio de e-mails.
- Nodemailer via
@nest-modules/mailerpara confirmação de e-mail e notificações.
- NestJS com DDD.
- Arquitetura baseada em CQRS com ****
@nestjs/cqrs. - Integração com @nestjs/trpc.
- Drizzle ORM com PostgreSQL.
- Validação com Zod-first.
- EventBus desacoplado, com Inngest como primeira implementação.
- Casos de uso como
UserItemUseCase, comUserItemCommandeUserItemQuery.
-
React Native Reusables para componentes.
-
Modais com
@gorhom/react-native-bottom-sheet. -
Listagem infinita com React Query.
-
Telas:
- Itens que o usuário possui.
- Itens que ainda não possui.
-
UI inspirada no inventário de Skyrim.
-
Login com BetterAuth Expo.
- Cadastro e edição de itens com React Hook Form.
- Interface com ShadCN UI (via Shad CDN ou local).
- Suporte a múltiplos administradores.
- Autenticação moderna com BetterAuth.
- Confirmação de e-mail e recuperação de senha com Inngest.
- Backend modular com NestJS + DDD + CQRS.
- EventBus desacoplado e substituível (começando com Inngest).
- Integração fullstack com TRPC e Zod.
- Listagem de inventário com filtros e paginação.
- Experiência inspirada em RPG para o mobile.
- Suporte a múltiplos usuários e permissões.
- Baseado no template
create-t3-turbo.
Imagine estar construindo o sistema de inventário de um jogo como Skyrim, mas com a robustez de um backend em NestJS com DDD + CQRS, a flexibilidade do React Native, e a segurança de uma autenticação moderna com BetterAuth. Esse é o nosso objetivo: criar uma experiência rica, intuitiva e escalável tanto para jogadores quanto para administradores.
Os itens no inventário podem ser de quatro tipos principais:
apparel— Equipamentos de vestuário (armaduras).weapons— Armas.consumables— Itens consumíveis.misc— Itens diversos (não utilizáveis).
-
Apparel:
chesthelmetbootsgloves
-
Weapons:
one-handedtwo-handed
A aplicação deve conter um caso de uso chamado UseItemUseCase, com as seguintes regras:
- Quando um item do tipo
apparelé usado:- O respectivo slot do usuário deve ser atualizado no banco de dados (ex:
equippedHelmet,equippedChest, etc). - Se já houver um item no slot correspondente, ele deve ser substituído pelo novo.
- Se o item usado já estiver equipado, ele deve ser desequipado (o slot deve ser limpo).
- O respectivo slot do usuário deve ser atualizado no banco de dados (ex:
- O usuário possui dois slots de mão:
leftHanderightHand. - Ao usar um item
one-handed:- Se
leftHandestiver livre, equipar ali. - Caso contrário, se
rightHandestiver livre, equipar ali. - Caso ambos estejam ocupados:
- O item atual de
leftHanddeve ser movido pararightHand. - O novo item deve ser colocado em
leftHand.
- O item atual de
- Se
- Se o item já estiver equipado em alguma mão, ele deve ser desequipado daquela mão.
- Quando um
consumableé usado:- Ele deve ser removido do inventário.
- Seu efeito deve ser aplicado diretamente no status do usuário.
- Os três consumíveis obrigatórios:
poção de vida→ aumenta o HP do usuário.poção de stamina→ aumenta o SP do usuário.poção de magia→ aumenta o MP do usuário.
- Itens do tipo
miscnão podem ser usados. - Ao tentar utilizar um item
misc, um erro deve ser retornado ao usuário (CannotUseItemErrorou similar).
A tela que exibe itens que o usuário ainda não possui funcionará como uma loja in-game, onde os usuários podem comprar novos itens usando o frontend mobile (React Native).
- Quando o usuário realizar a compra de um item:
- O item será adicionado ao inventário do usuário.
- Um e-mail de confirmação de compra será enviado imediatamente.
- O e-mail deve ser renderizado utilizando React Email.
- O projeto já possui uma estrutura base para e-mails — este novo e-mail deve ser integrado a ela.
- O componente deve seguir o estilo visual dos e-mails já existentes e conter:
- Nome do item comprado.
- Imagem (se aplicável).
- Texto de agradecimento pela compra.
- Instruções de onde encontrar o item dentro do app.
Exemplo de estrutura:
import { Html, Text, Heading, Img, Button } from '@react-email/components';
export const PurchaseConfirmationEmail = ({ itemName, imageUrl }: { itemName: string; imageUrl: string }) => (
<Html>
<Heading>Obrigado pela sua compra!</Heading>
<Text>Você adquiriu o item <strong>{itemName}</strong> com sucesso.</Text>
{imageUrl && <Img src={imageUrl} alt={itemName} width="200" />}
<Text>Ele já está disponível no seu inventário dentro do aplicativo.</Text>
<Button href="https://seu-app.com/app/inventario">Ver meu inventário</Button>
</Html>
);