| (25 revisões intermediárias por 2 usuários não estão sendo mostradas) | |||
| Linha 15: | Linha 15: | ||
[RF002] Implementar match | [RF002] Implementar match | ||
== Requisitos Não-Funcionais == | == Requisitos Não-Funcionais == | ||
<br> | <br> | ||
[RNF] O sistema deve possuir respostas rápidas com menos de 1s de latência | |||
== Melhores práticas == | == Melhores práticas == | ||
| Linha 24: | Linha 24: | ||
<b>Single Responsibility Principle - Responsabilidade Única</b> | <b>Single Responsibility Principle - Responsabilidade Única</b> | ||
A classe <code>class UserRepository(BaseRepository)</code> | A classe <code>class UserRepository(BaseRepository)</code> possui métodos de responsabilidade única como mostrado abaixo: | ||
<pre> | <pre> | ||
def get_user_by_id(self, user_id: int) -> Optional[User]: | def get_user_by_id(self, user_id: int) -> Optional[User]: | ||
| Linha 37: | Linha 36: | ||
<b>Open/Closed Principle - Aberto para Extensão, Fechado para Modificação</b> | <b>Open/Closed Principle - Aberto para Extensão, Fechado para Modificação</b> | ||
A classe <code>class UserService</code> tem métodos que estão fechados para modificação mas a classe pode ser extendida adicionando novos métodos, como mostrado abaixo: | |||
<pre> | <pre> | ||
def | class UserService: | ||
return | def __init__(self, user_repository: UserRepository): | ||
self.user_repository = user_repository | |||
def create_user(self, name: str, email: str, senha: str, birthday: Date, profile_picture_path: str, description: str = None) -> User: | |||
# Verificar se já existe um usuário com este e-mail | |||
existing_user = self.user_repository.get_user_by_email(email) | |||
if existing_user: | |||
raise ValueError("Este e-mail já está cadastrado") | |||
# Hash the password before storing | |||
hashed_password = bcrypt.hashpw(senha.encode('utf-8'), bcrypt.gensalt()) | |||
return self.user_repository.create_user( | |||
name=name, | |||
email=email, | |||
senha=hashed_password.decode('utf-8'), | |||
birthday=birthday, | |||
profile_picture_path=profile_picture_path, | |||
description=description | |||
) | |||
def get_user_by_id(self, user_id: int) -> Optional[User]: | |||
return self.user_repository.get_user_by_id(user_id) | |||
def update_user(self, user_id: int, **kwargs) -> Optional[User]: | |||
return self.user_repository.update_user(user_id, **kwargs) | |||
def delete_user(self, user_id: int) -> bool: | |||
return self.user_repository.delete_user(user_id) | |||
def get_user_by_email(self, email: str) -> Optional[User]: | |||
"""Busca um usuário pelo email.""" | |||
return self.user_repository.get_user_by_email(email) | |||
def authenticate_user(self, email: str, senha: str) -> Optional[User]: | |||
"""Autentica um usuário verificando email e senha""" | |||
user = self.user_repository.get_user_by_email(email) | |||
if not user: | |||
return None | |||
if not bcrypt.checkpw(senha.encode('utf-8'), user.senha.encode('utf-8')): | |||
return None | |||
return user | |||
</pre> | </pre> | ||
<b>Liskov Substitution Principle - Substituição de Liskov</b> | <b>Liskov Substitution Principle - Substituição de Liskov</b> | ||
Todas as classes que fazem parte de um repositório herdam/implementam <code>BaseRepository</code>, como exemplo temos a classe <code>UserRepository</code>. | |||
<pre> | <pre> | ||
def | class UserRepository(BaseRepository): | ||
def __init__(self, session: Session): | |||
super().__init__(session) | |||
class BaseRepository: | |||
""" | |||
Repositório base que recebe uma sessão como dependência. | |||
Segue o princípio de Inversão de Dependência. | |||
""" | |||
def __init__(self, session: Session): | |||
""" | |||
Inicializa o repositório com uma sessão do banco de dados. | |||
Args: | |||
session: Sessão do SQLAlchemy gerenciada externamente | |||
""" | |||
self.session = session | |||
def execute_stmt(self, stmt: Any): | |||
""" | |||
Executa um statement SQLAlchemy usando a sessão injetada. | |||
Args: | |||
stmt: Statement SQLAlchemy (select, insert, update, delete, etc.) | |||
Returns: | |||
Resultado da execução do statement | |||
""" | |||
return self.session.execute(stmt) | |||
</pre> | </pre> | ||
<b>Interface Segregation Principle - Segregação de Interfaces</b> | <b>Interface Segregation Principle - Segregação de Interfaces</b> | ||
A linguagem Python não implementa interfaces que expõe métodos, com isso não foi possível aplicar esse princípio no projeto | |||
<b>Dependency Inversion Principle - Inversão de Dependência</b> | <b>Dependency Inversion Principle - Inversão de Dependência</b> | ||
Mesmo sem a presença se interfaces o que facilita a inversão de depedência, conseguimos aplicar este princípio criando um classe referente a repositório que faz a instanciação do banco de dados assim que a classe é criada não sendo necessário repetir o código novamente. | |||
<pre> | <pre> | ||
def | class UserRepository(BaseRepository): | ||
def __init__(self, session: Session): | |||
super().__init__(session) | |||
class BaseRepository: | |||
""" | |||
Repositório base que recebe uma sessão como dependência. | |||
Segue o princípio de Inversão de Dependência. | |||
""" | |||
def __init__(self, session: Session): | |||
""" | |||
Inicializa o repositório com uma sessão do banco de dados. | |||
Args: | |||
session: Sessão do SQLAlchemy gerenciada externamente | |||
""" | |||
self.session = session | |||
def execute_stmt(self, stmt: Any): | |||
""" | |||
Executa um statement SQLAlchemy usando a sessão injetada. | |||
Args: | |||
stmt: Statement SQLAlchemy (select, insert, update, delete, etc.) | |||
Returns: | |||
Resultado da execução do statement | |||
""" | |||
return self.session.execute(stmt) | |||
</pre> | </pre> | ||
= | |||
<b>Clean Code</b> | |||
O código foi pensado para possuir métodos simples e fáceis de entender e dar manutenção, além de possuir variáveis com nomes que refletem as propriedades do objeto facilitando assim o entendimento da lógica do código. | |||
= CRONOGRAMA = | |||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
! Item !! Data !! Atividades Room Match !! Realizado | ! Item !! Data !! Atividades Room Match !! Realizado | ||
|- | |- | ||
| 1 || 14/11/2025 || Documentar tópico Investigação || 0% | | 1 || 14/11/2025 || Documentar tópico Investigação || 0% | ||
|- | |- | ||
| 2 || 14/11/2025 || Definir Proposta de Projeto || 0% | | 2 || 14/11/2025 || Definir Proposta de Projeto || 0% | ||
|- | |- | ||
| 3 || 14/11/2025 || Validar Visão do Usuário || 0% | | 3 || 14/11/2025 || Validar Visão do Usuário || 0% | ||
|- | |- | ||
| 4 || 14/11/2025 || Especificar RFs e RNFs - Fase 2 || 100% | | 4 || 14/11/2025 || Especificar RFs e RNFs - Fase 2 || 100% | ||
|- | |- | ||
| 5 || 17/11/2025 || RF01: Implementar visualização de detalhes || | | 5 || 17/11/2025 || RF01: Implementar visualização de detalhes || 20% | ||
|- | |- | ||
| | | x || 24/11/2025 || TeckWeek || | ||
|- | |||
| 6 || 01/12/2025 || Melhores Práticas || 100% | |||
|- | |- | ||
| | | 7 || 01/12/2025 || RF01: Implementar visualização de detalhes da vaga || 100% | ||
|- | |- | ||
| | | 8 || 01/12/2025 || RF02: Implementar matching || 35% | ||
|- | |- | ||
| 9 || 08/12/2025 || RF03: Implementar visualização de detalhes do usuário || 100% | |||
|- | |||
| 10 || 08/12/2025 || RF04: Implementar guardrail listagem de usuários || 20% | |||
|- | |||
| 11 || 08/12/2025 || RF05: Implementar cálculo de score baseado nas preferências || 25% | |||
|- | |||
| 12 || 08/12/2025 || RF06: Implementar criação de chat sem comunicação síncrona || 0% | |||
|- | |||
| 13 || 15/12/2025 || 2a. entrega - Video pelo Teams até 19/12/2025 - RFs 1, 2 e 3 || | |||
|- | |||
| 14 || || Incrementar diferencial tecnológico || | |||
|- | |||
| 15 || 19/12/2025 || Vídeo demo encaminhado para cliente || Aguardando avaliação | |||
|- | |||
|} | |||
{| class="wikitable" | |||
|- | |||
! Item !! Data !! Atividades Room Match !! Responsável | |||
|- | |||
| 1 || 09/02/2026 || xx || | |||
|- | |||
| 2 || 23/02/2026 || xx || | |||
|- | |||
| 3 || 02/03/2026 || xx || | |||
|- | |||
| 4 || 09/03/2026 || xx || | |||
|- | |||
| 5 || 16/03/2026 || xx || | |||
|- | |||
|} | |} | ||
Edição atual tal como às 20h57min de 9 de fevereiro de 2026
Fase 2
Escopo
- Desenvolver um sistema que facilite o encontro entre pessoas que estão à procura de um quarto para alugar e as que já possuem o quarto disponível para locação em seus imóveis
- A proposta é criar uma plataforma digital (via web) que funcione como um matchmaking de moradia, similar ao funcionamento de apps de relacionamento, com base em critérios como localização, orçamento, perfil de convivência e preferências pessoais
Requisitos Funcionais
[RF001] Implementar visualização de detalhes (quarto e perfil)
[RF002] Implementar match
Requisitos Não-Funcionais
[RNF] O sistema deve possuir respostas rápidas com menos de 1s de latência
Melhores práticas
Single Responsibility Principle - Responsabilidade Única
A classe class UserRepository(BaseRepository) possui métodos de responsabilidade única como mostrado abaixo:
def get_user_by_id(self, user_id: int) -> Optional[User]:
"""Busca um usuário pelo ID."""
stmt = select(User).where(User.id == user_id)
result = self.execute_stmt(stmt)
return result.scalar_one_or_none()
Essa função tem uma única responsabilidade que é obter o usuário do repositório não aplicando sobre o usuário nenhum tipo de regra de negócio ou modificação.
Open/Closed Principle - Aberto para Extensão, Fechado para Modificação
A classe class UserService tem métodos que estão fechados para modificação mas a classe pode ser extendida adicionando novos métodos, como mostrado abaixo:
class UserService:
def __init__(self, user_repository: UserRepository):
self.user_repository = user_repository
def create_user(self, name: str, email: str, senha: str, birthday: Date, profile_picture_path: str, description: str = None) -> User:
# Verificar se já existe um usuário com este e-mail
existing_user = self.user_repository.get_user_by_email(email)
if existing_user:
raise ValueError("Este e-mail já está cadastrado")
# Hash the password before storing
hashed_password = bcrypt.hashpw(senha.encode('utf-8'), bcrypt.gensalt())
return self.user_repository.create_user(
name=name,
email=email,
senha=hashed_password.decode('utf-8'),
birthday=birthday,
profile_picture_path=profile_picture_path,
description=description
)
def get_user_by_id(self, user_id: int) -> Optional[User]:
return self.user_repository.get_user_by_id(user_id)
def update_user(self, user_id: int, **kwargs) -> Optional[User]:
return self.user_repository.update_user(user_id, **kwargs)
def delete_user(self, user_id: int) -> bool:
return self.user_repository.delete_user(user_id)
def get_user_by_email(self, email: str) -> Optional[User]:
"""Busca um usuário pelo email."""
return self.user_repository.get_user_by_email(email)
def authenticate_user(self, email: str, senha: str) -> Optional[User]:
"""Autentica um usuário verificando email e senha"""
user = self.user_repository.get_user_by_email(email)
if not user:
return None
if not bcrypt.checkpw(senha.encode('utf-8'), user.senha.encode('utf-8')):
return None
return user
Liskov Substitution Principle - Substituição de Liskov
Todas as classes que fazem parte de um repositório herdam/implementam BaseRepository, como exemplo temos a classe UserRepository.
class UserRepository(BaseRepository):
def __init__(self, session: Session):
super().__init__(session)
class BaseRepository:
"""
Repositório base que recebe uma sessão como dependência.
Segue o princípio de Inversão de Dependência.
"""
def __init__(self, session: Session):
"""
Inicializa o repositório com uma sessão do banco de dados.
Args:
session: Sessão do SQLAlchemy gerenciada externamente
"""
self.session = session
def execute_stmt(self, stmt: Any):
"""
Executa um statement SQLAlchemy usando a sessão injetada.
Args:
stmt: Statement SQLAlchemy (select, insert, update, delete, etc.)
Returns:
Resultado da execução do statement
"""
return self.session.execute(stmt)
Interface Segregation Principle - Segregação de Interfaces
A linguagem Python não implementa interfaces que expõe métodos, com isso não foi possível aplicar esse princípio no projeto
Dependency Inversion Principle - Inversão de Dependência
Mesmo sem a presença se interfaces o que facilita a inversão de depedência, conseguimos aplicar este princípio criando um classe referente a repositório que faz a instanciação do banco de dados assim que a classe é criada não sendo necessário repetir o código novamente.
class UserRepository(BaseRepository):
def __init__(self, session: Session):
super().__init__(session)
class BaseRepository:
"""
Repositório base que recebe uma sessão como dependência.
Segue o princípio de Inversão de Dependência.
"""
def __init__(self, session: Session):
"""
Inicializa o repositório com uma sessão do banco de dados.
Args:
session: Sessão do SQLAlchemy gerenciada externamente
"""
self.session = session
def execute_stmt(self, stmt: Any):
"""
Executa um statement SQLAlchemy usando a sessão injetada.
Args:
stmt: Statement SQLAlchemy (select, insert, update, delete, etc.)
Returns:
Resultado da execução do statement
"""
return self.session.execute(stmt)
Clean Code
O código foi pensado para possuir métodos simples e fáceis de entender e dar manutenção, além de possuir variáveis com nomes que refletem as propriedades do objeto facilitando assim o entendimento da lógica do código.
CRONOGRAMA
| Item | Data | Atividades Room Match | Realizado |
|---|---|---|---|
| 1 | 14/11/2025 | Documentar tópico Investigação | 0% |
| 2 | 14/11/2025 | Definir Proposta de Projeto | 0% |
| 3 | 14/11/2025 | Validar Visão do Usuário | 0% |
| 4 | 14/11/2025 | Especificar RFs e RNFs - Fase 2 | 100% |
| 5 | 17/11/2025 | RF01: Implementar visualização de detalhes | 20% |
| x | 24/11/2025 | TeckWeek | |
| 6 | 01/12/2025 | Melhores Práticas | 100% |
| 7 | 01/12/2025 | RF01: Implementar visualização de detalhes da vaga | 100% |
| 8 | 01/12/2025 | RF02: Implementar matching | 35% |
| 9 | 08/12/2025 | RF03: Implementar visualização de detalhes do usuário | 100% |
| 10 | 08/12/2025 | RF04: Implementar guardrail listagem de usuários | 20% |
| 11 | 08/12/2025 | RF05: Implementar cálculo de score baseado nas preferências | 25% |
| 12 | 08/12/2025 | RF06: Implementar criação de chat sem comunicação síncrona | 0% |
| 13 | 15/12/2025 | 2a. entrega - Video pelo Teams até 19/12/2025 - RFs 1, 2 e 3 | |
| 14 | Incrementar diferencial tecnológico | ||
| 15 | 19/12/2025 | Vídeo demo encaminhado para cliente | Aguardando avaliação |
| Item | Data | Atividades Room Match | Responsável |
|---|---|---|---|
| 1 | 09/02/2026 | xx | |
| 2 | 23/02/2026 | xx | |
| 3 | 02/03/2026 | xx | |
| 4 | 09/03/2026 | xx | |
| 5 | 16/03/2026 | xx |