Tentativa de recriar uma api com a maioria das funcionalidades do twitter e que seja escalável.
- Auth-Gateway: Gateway de Autenticação
- Client-Service: Serviço que cuida dos Usuarios (Em desenvolvimento)
- Tweet-Service: Servico que cuida dos Tweets do Usuario (Em desenvolvimento)
- Follow-Service: Servico que cuida da lógica de follow (Planejado)
- Profile-Service: Servico que cuida do Feed dos Usuarios (Planejado)
- Feed-Service: Servico que cuida do Feed dos Usuarios (Planejado)
- Search-Service: Servico que cuida do mecanismo de busca de usuario (Planejado)
- Chat-Service: Serviço que cuida da troca de mensagens entre usuarios
- Estudos
- [High-Level-Design] (#high-level-design)
- Entitdades
- Requirements
- Especificacoes
- Dados-do-Twitter
- Endpoints
()
Usuario:
Cadastro:
- Usuario consegue se cadastrar
- Usuario recebe código de validação de email
- Usuario consegue pedir reenvio da validação de email
- Usuario consegue trocar o email de confirmação
- Usuario consegue validar seu email com código e ter sua conta criada
Login:
- Usuario consegue logar
- Usuario consegue editar dados de perfil
- Usuario consegue editar a senha quando esquecida
- Usuario consegue deletar seu perfil
Profile:
- Usuario consegue ver suas informações, seus posts e posts compartilhados
- Usuario consegue ver seus comentários em outros posts
- Usuario consegue ver seus posts curtidos
- Usuario consegue ver posts e posts compartilhados de um Usuario especifico
Follow:
- Usuario consegue seguir outros Usuarios
Tweet:
- Usuario consegue fazer um post
- Usuario consegue deletar um post
- Usuario consegue curtir um post
- Usuario consegue descurtir um post
- Usuario consegue compartilhar um post
- Usuario consegue descompartilhar um post
- Usuario consegue comentar em um post
- Usuario consegue apagar o comentário de um post
- Usuario consegue ver comentários de um post
- Usuario consegue ver comentários de um comentário
Search:
- Usuario consegue pesquisar outros Usuarios pelo nome
Feed:
- Usuario consegue ver o seu feed
-
Limite de 140 Caracteres
-
Suporta Video
-
Suporta Imagem
-
Os detalhes possuem uma camada de comentarios
-
O usuario consegue ver no Post se ele já foi curtido ou compartilhado
-
Os detalhes do comentário mostram 2 graus de profundidade se for um reply feito pelo usuario criador do comentário
-
É possivel ver uma thread inteira de comentários de um usuario
- Os ultimos posts dos followers do usuario
- 500M Usuarios totais
- 200M de Usuarios Ativos
- 40M de Posts por Dia
- 1 Usuario lê 100 tweets por dia
- 1 MB de dados por Tweet
- 1 mês 18TB de Dados salvos no banco
Conclusões:
-
O maior problema é armazenamento
-
Informação em tempo real não é tão importante
-
A escrita e leitura de dados são leves mas muito numerosas
-
Manter o Feed atualizado é o maior desafio
Soluções:
-
MongoDb para escalar os dados horizontalmente
-
CDN para salvar as fotos e vídeos usando o algoritimo PULL
-
Criar os feeds dos usuarios asincronamente e salva em um cache para deixar disponivel instantaneamente
-
Salvar profiles muito acessados em cache
- Em rotas pesadas punir os ips mais severamente
- Cliente: request Http
- Api: registerUser(name, email, password): preUserId
- Verifica se já existe usuarios com esse email ou nome no banco
- Verifica se já existe um preUsuario com esse nome, se sim retorna erro
- Verifica se já existe um preUsuario com email. Se sim deleta registro do cache
- Cria registro no cache com os dados do usuario que expira em 2 horas
- Verifica se já existe um registro de validação de email
- Se sim:
- pega o código enviado
- Adiciona uma nova tentativa a essa validação
- Se não:
- Cria registro de validação de email no cache que expira em 2 horas com um novo código de validação
- Se sim:
- Envia email para o email cadastrado com o código de validação
- Banco: MongoDb
- Cache: Redis
- Cliente: Request HTTP
- Api: editValidationEmail(userId, newEmail)
- Verifica se já existe algum usuario cadastro com esse email
- Verifica se existe algum preUser no cache com aquele email, se sim retorna erro.
- Verfica se o preUser existe
- Verifica se não existe nenhum usuario com esse email
- Atualiza o preUser
- Atualiza o emailValidation do preUser se ele existe
- Cache: Redis
- Cliente: Request HTTP
- Api: validateCode(userEmail, validationCode, operationValidatedType): createAccountJWT
- Rota autenticada com JWT para criação
- Api: createAccount(preUserId): JWTLogin
- Verfica se o preUser existe
- Verifica a validade do código e sua correspondencia
- Cria usuario
- Cache: Redis
- Cliente: Request HTTP
- Api: resendValidationCode(userId)
- Verifica se o usuario já pediu essa ação mais de 5 vezes nos ultimos 30 minutos
- Verifica se no cache existe um registro de validação de email com um código
- Caso sim:
- Envia um email com ele
- Caso não:
- Cria um novo registro de Validação de email
- Envia um email com ele
- Cache: Redis
- Cliente: Request HTTP
- Api: login(email, password): token
- Verifica em cache se já é a 5 tentiva de login para aquele email
- Verifica se esse email está cadastrado
- Verifica senha passada
- Validação falha:
- Gera registro ou atualiza registro de tentativa de login para aquele usuario que expira em 10 minutos
- Validação Passa: - Retorna token JWT de acesso
- Cache: Redis
- Cliente: Request HTTP
- Rota: Autenticada
- Api: uploadMedia(userId, media): s3Url
- Baixa foto na S3
- Deixa disponivel no CDN
- Api: registerUserInfo(bannerPictureS3Url, profilePictureS3Url, bio, location, website, birthDate, profileName)
- Cliente: Request HTTP
- Rota: Autenticada
- Api: validatePassword(password): passwordUpdateJWT
- Rota: Autenticada para password update
- Api: updatePassword(userId, newPassword)
- Verifica se o usuario existe
- Verifica se a password antiga passada bate com a salva no banco
- Verifica se a nova senha é diferente da antiga
- Verifica se a nova senha é diferente de
- Atualiza a password da senha
- Cliente: Request HTTP
- Rota: Autenticada
- Api: forgotPassword(userEmail)
- Verifica se usuario existe
- Verifica se já existe um código de validação enviado para alterar a senha
- Se sim: Envia email com mesmo código de validação
- Se não: Envia novo email com novo código de validação
- Cliente: request Http
- Rota autenticada
- Api: getUserPageInfo(userId, page, page.size = 20)
- Verifica se existe um profile cacheado para esse usuario e se o take bate com o salvo no cache
- Se não busca no banco de dados os dados do usuario e salva em cache que dura 2 minutos
- Retorno: { UserInfo, posts: Post[]}
- Databae: MongoDb
- Cache: Redis
Dicussão:
- Qual o custo de cachear sempre, vale a pena ter alguma regra de negocio para defini-lo?
- R: Sim, pois impede usuarios de fazerem muitas requisições para dados que não precisam estar 100% atualizados.
- Quanto tempo deixaremos em cache os profiles?
- Qual o impacto do tempo de cache?
- Tem problema o profile ficar desatualizado durante 2 minutos?
- Cliente: Request Http
- Rota autenticada
- Api: getPostsByUserComments(userId)
- Pegar os ultimos comentários do usuario
- Encontrar o post relacionado a eles
- Listar os ultimos 10
- Retorno: [{ ...Post, nestedComentaries: [{ ...Commentary, depth: 1, { ...comentaryInfo, depth: 2}}] }]
Suposições:
- Não é uma rota muito usada
- Cliente: Request Http
- Rota autenticada
- Api: getPostsByUserLikes(userId, page, pageSize)
- Pegar os ulitmos likes do usuario
- Encontrar o post relacionado a eles
- Retorno: Post[]
- Database: MongoDb
Suposições:
- Não é uma rota muito usada
- Cliente: Request Http
- Rota autenticada
- Api: follow(userId, toFollowUserId)
- Cria registro do follow na tb_follow
- Envia ao Kafka o evento de usuario seguido
- Database: LevelDB ou NeoJs
- Cliente: Request Http
- Rota autenticada
- Api: media(userId)
- Salva media no S3
- Deixa disponivel no CDN
- Api: makePost(userId, content)
- Salva media no s3
- Salva o post no database
- Salva o post no cache do redis
- Envia um evento para o Kafka de criação de Post
- Banco: MongoDb
-
Cliente: Request Http
-
Rota autenticada
-
Api: deletePost(userId, postId)
- Marca deleted_at na tb_post
- Manda um evento para o Kafka de deleção de Post
- Cliente: Request Http
- Rota autenticada
- Api: likePost(userId, postId)
- Atualiza o post_id adicionando a doble-liked-list de likes
- Envia um evento ao Kafka de post-curtido
Suposição:
- Um post pode chegar a ter 1m de likes
- Um post tem em media 10k de likes
- Cliente: Request Http
- Rota autenticada
- Api: unlikePost(userId, postId)
- Atualiza o post_id removendo o like do usuario
- Envia um evento ao Kafka de post-ulinked
Suposição:
- Um post pode chegar a ter 1m de likes
- Um post tem em media 10k de likes
- Cliente: Request Http
- Rota autenticada
- Api: sharePost(userId, postId, shareContent)
- Cria um registro tb_share com o post_id
- Invalida o cache do post
- Database: MongoDb
- Cliente: Request Http
- Rota autenticada
- Api: sharePost(userId, postId)
- Cria um registro tb_share com o post_id
- Invalida o cache do post
- Database: MongoDb
- Cliente: Request Http
- Rota autenticada
- Api: likePost(userId, postId)
- Cria um registro na tb_comentario para aquele post_id
- Invalida o cache do post
- Database: MongoDb
- Cliente: Request Http
- Rota autenticada
- Api: getCommentsByPostId(postId)
- Faz query pegando as informações do post e os comentarios atrelados aquele PostId
- retorno: Comments[]
- Banco: MongoDb
- Cliente: Request Http
- Rota autenticada
- Api: getCommentsByCommentaryId(commentaryId)
- Pega os comentarios no banco pelo commentaryId
- retorno: Comment[]
- Cliente: request Http
- Rota autenticada
- Api: searchUserByName(name): userId
- Retorna
- Cliente: Request Http
- Rota autenticada
- Api: getFeed(userId)
- Pega do cache o feed
- retorno: Post[]
- Banco: MongoDb
-
Kafka-Consumers-deleted-post:
- Encontram todos os usuarios que seguem o autor do post deletado
- Envia para o redis-channel a informação de que eles precisam ter seus feeds atualizados
-
Redis-channel-consumer-deletion:
- Redis-channel é consumido deletando o ultimo post do chache do Redis
- busca no banco de dados o ultimo post do seguidor
- Adiciona no final da doble-linked-list esse post
-
Kafka-Consumers-deleted-post:
- Encontram todos os usuarios que seguem o autor do post deletado
- Envia para o redis-channel a informação de que eles precisam ter seus feeds atualizados
-
Redis-channel-consumer-deletion:
- Redis-channel é consumido deletando o ultimo post do chache do Redis
- busca no banco de dados o ultimo post do seguidor
- Adiciona no final da doble-linked-list esse post
Post: { id, content: string, imageUrl?, videoUrl?, likeNumber, sharesNumber, commentariesNumber, wasLikedByUser: bool, wasSharedByUser: bool, createdAt: Date, userInfo: UserInfo }
Comment: Post
UserInfo: { id: string, profilePictureUrl: string, name: string, bio: string}
Twitter system design mock interview (https://www.youtube.com/watch?v=3yW856jAbZA) System Design Twitter (https://www.youtube.com/watch?v=_QqpD0w8oPM) Twitter System Design (https://www.youtube.com/watch?v=wYk0xPP_P_8)
