- Diagramas de Sequência dos Requisitos Funcionais
- TOC
- Notas sobre API
- RF-01 - Navegação pelos produtos através da hierarquia de categorias
- RF-02: Visualização de produtos e seus fornecedores
- RF-03: Pesquisa de produtos através dos campos comuns a todos os produtos
- RF-04: Colocação, consulta, e remoção de produtos num cesto de compra
- RF-05: Criação de uma conta no sistema
- RF-06: Fazer login no sistema
- RF-07: Edição dos dados e remoção da sua conta no sistema
- RF-08: Pesquisa de produtos através dos campos específicos das categorias
- RF-09: Visualização do histórico de encomendas e seus detalhes
- RF-10: Comparação de dois produtos, com as diferenças em destaque
- RF-11: Encomenda dos produtos no cesto de compras
- RF-12: Pagamento de encomenda recorrendo a um sistema externo
- RF-13: Cancelamento de encomenda, desde que dentro de um dado prazo
- RF-14: Notificação sobre a saída de produtos encomendados de um fornecedor
- RF-15: Notificação sobre a chegada iminente de encomenda
- RF-16: Visualização de relatório do impacto local das suas encomenda
- RF-17: Exportação dos dados das encomendas para ficheiros JSON
- RF-18: Criação, gestão, e remoção de unidade de produção
- RF-19: Criação, gestão, e remoção de produto, e ligação a unidade de produção
- RF-20: Visualização de unidade de produção e dos seus produtos
- RF-21: Criação, edição, e remoção de veículo de transporte de produtos
- RF-22: Notificação sobre encomenda de consumidor
- RF-23: Visualização de encomenda de consumidor
- RF-24: Colocação de produto encomendado em veículo de transporte disponível
- RF-25: Registo de saída de veículo de transporte com produtos encomendados
- RF-26: Registo de chegada iminente de encomenda a casa do consumidor
- RF-27: Visualização de relatório do impacto local das vendas dos seus produtos
- RF-28: Desativação e reativação de qualquer conta de utilizador no sistema
- RF-29: Visualização de relatório do impacto local das vendas de produtos
Exemplo:
filter[address][city]=London&
filter[address][street]=12+High+Street&
filter[location][]=10&
filter[location][]=20&
filter[name]=David&
filter[nationality]=Danish
irá construir o seguinte filtro:
filter: {
"name": "David",
"nationality": "Danish",
"address": {
"street": "12 High Street",
"city": "London",
},
"location": [10, 20],
}
Na API temos o endpoint GET /categories
para obter as categorias na sua forma hierárquica.
A partir desse temos conhecimento dos IDs aos quais nos iremos referir no filtro.
O endpoint a usar para listar os ProductSpec
é /products
Exemplo: /products?filter[categoryId][]=1&filter[categoryId][]=2
sequenceDiagram
actor A as Alice
participant API
participant CG as CategoryGateway
participant PSG as ProductSpecGateway
A ->> API: GET /categories
API ->> CG : getTree()
CG -->> API: category[]
API --> A: category[]
Note right of A: Alice wants the products from the categories with ID 1 and 2
A ->> API: GET /products?filter[categoryId][]=1&filter[categoryId][]=2
API ->> PSG: findFromCategory([1, 2])
PSG -->> API: products[]
API -->> A: products[]
Interpretação: Visualização da especificação dos produtos [e noutra página] dos fornecedores que vendem essa especificação. No máximo teríamos um ou dois fornecedores que vendem cada produto na lista de produtos.
A interface irá ser semelhante a:
- Uma lista principal, paginada, de
ProductSpec
, com preço mínimo, máximo, e médio dos fornecedores. - Após clicar num
ProductSpec
, irá abrir uma página com mais detalhes sobre oProductSpec
tais como uma descrição mais detalhada e os valores dos fields das categorias. Terá de ter também um botão para comparar com outros produtos. Poderá, eventualmente, ter o histórico dos preços dos diversos fornecedores.- Irá ter também a lista de
ProducerProduct
, mostrando o fornecedor e o preço. Ao clicar somos redirecionados para a página doProducerProduct
.
- Irá ter também a lista de
- A página do
ProducerProduct
terá o histórico de preços desse produto e outras informações pertinentes, se bem que já deve estar tudo na página doProductSpec
, tornando a página atual mais direcionada à compra.
Através do endpoint GET /products
podemos obter os ProductSpec
.
Contudo, para obter todos os ProducerProduct
(e por consequência os fornecedores) de um ProductSpec
podemos usar o endpoint /products/{id}/products
que nos devolve todos os fornecedores que fornecem esse produto.
sequenceDiagram
actor A as Alice
participant API
participant PSG as ProductSpecGateway
participant PPG as ProducerProductGateway
A ->> API: GET /products
API ->> PSG: find()
PSG -->> API: productSpec[]
API -->> A: productSpec[]
Note right of A: Alice wants to see the Producers of the ProductSpec with ID 1
A ->> API: GET /products/1/products
API ->> PPG: findFromSpec(1)
PSG -->> API: producerProduct[]
API -->> A: producerProduct[]
Note right of A: producerProduct[] contains the Producer object
Podemos usar o endpoint GET /products/search?q=
, definindo o parâmetro q
com o termo que queremos pesquisar. A pesquisa será relativa as campos comuns a todos os produtos, sendo estes o nome, TODO completar.
sequenceDiagram
actor A as Alice
participant API
participant PSC as ProductSpecGateway
A ->> API: GET /products/search?q=iphone
API ->> PSC: find("iphone")
PSC -->> API: productSpec[]
API -->> A: productSpec[]
Para colocar um ProducerProduct
no carrinho de compras podemos usar o endpoint /consumers/{consumerId}/cart/products/{productId}
com o método PUT
, em que productId
é o id do ProducerProduct
que queremos colocar no carrinho.
sequenceDiagram
actor A as Alice
participant API
participant CaG as CartGateway
participant PPG as ProducerProductGateway
participant CaI as CartItem
participant CaIG as CartItemGateway
A ->> API: PUT /consumers/{consumerId}/cart/products/1
API ->> CaG: get(consumerId)
CaG -->> API: cart
API ->> PPG: get(1)
PPG -->> API: producerProduct
Note right of A: Alice wants to add the ProducerProduct with ID 1 to her cart
API ->> CaI: new(cart, producerProduct, quantity)
CaI -->> API: cartItem
API ->> CaIG: insert(cartItem)
CaIG -->> API: savedCartItem
API -->> A: savedCartItem
Para consultar os produtos no carrinho podemos usar o endpoint /consumers/{consumerId}/cart/products
com o método GET
.
sequenceDiagram
actor A as Alice
participant API
participant CaG as CartGateway
participant CaIG as CartItemGateway
A ->> API: GET /consumers/{consumerId}/cart/products
API ->> CaG: get(consumerId)
CaG -->> API: cart
API ->> CaIG: findFromCart(cart)
CaIG -->> API: cartItem[]
API -->> A: cartItem[]
Remover um produto do carrinho é feito através do endpoint /consumers/{consumerId}/cart/products/{productId}
com o método DELETE
.
sequenceDiagram
actor A as Alice
participant API
participant CaIG as CartItemGateway
A ->> API: DELETE /consumers/{consumerId}/cart/products/{producerProductId}
API ->> CaIG: get(consumerId, producerProductId)
CaIG -->> API: cartItem
API ->> CaIG: delete(cartItem)
CaIG -->> API: true
API -->> A: true
-
Criar uma conta sem credenciais relacionadas. Feito através dum
POST
a/consumers
ou/producers
com os dados necessários, sendo este endpoint não protegido. -
Após a criação do utilizador com sucesso, solicitar a adição de uma credencial (dos vários tipos
authType
possíveis:password
,google
,facebook
,twitter
)- Autenticar o utilizador no serviço externo
- Obtenção de um token do serviço externo
- Criação da credencial ao utilizador através de
POST
a/consumers/{consumerId}/credentials
ou/producers/{producerId}/credentials
, dependendo do tipo de utilizador.
- Até à adição da credencial, o utilizador ficará num estado em que apenas existe partialmente. Se não adicionarmos uma credencial, o utilizador não se poderá autenticar, ocupando recursos de forma desnecessária.
Iremos apenas contemplar a criação de um Consumer
, mas o processo é idêntico para um Producer
sequenceDiagram
actor A as Alice
participant API
participant C as Consumer
participant CG as ConsumerGateway
participant Au as AuthService
participant F as Firebase
participant Cred as Credential
participant CredG as CredentialGateway
participant EAu as ExternalAuthGateway
Note right of A: First, we show the user creation form
A ->> API: POST /consumers
API ->> C: new({name, email, ...})
C -->> API: consumer
API ->> CG: insert(consumer)
CG -->> API: savedConsumer
API ->> Au: createAccessToken(consumer)
Au -->> API: temporaryAccessToken
API -->> A: temporaryAccessToken
A ->> F: authenticate(email, password)
F -->> A: token
Note right of A: Then, user is asked to add a credential
Note right of A: token is sent in headers
A ->> API: POST /consumers/{consumerId}/credentials
API ->> Au: authenticate(temporaryAccessToken)
Au ->> CredG: get(temporaryAccessToken)
CredG -->> Au: consumer
Au -->> API: consumer
API ->> CredG: checkUnique(token)
CredG -->> API: true
API ->> Au: addCredential(consumer, {authType: "password", token})
Note right of Au: We may be able to verify locally, skipping this step
alt verify token
Au ->> EAu: verifyToken(extToken)
EAu -->> Au: extToken
end
Au ->> Cred: new({authType: "password", token})
Cred -->> Au: credential
Au ->> CredG: create(credential)
CredG -->> Au: credential
Au -->> API: credential
API -->> A: credential
Autenticar o utilizador no serviço externo através do frontend e obter um token do serviço externo.
Usar o endpoint /auth/login
com o método POST
, forneçendo o token no body de forma a trocá-lo por um access token do nosso sistema.
sequenceDiagram
actor A as Alice
participant F as Firebase
participant API
participant Au as AuthService
participant EAu as ExternalAuthGateway
participant CredG as CredentialGateway
participant CG as ConsumerGateway
participant C as Consumer
A ->> F: authenticate()
F -->> A: extToken
A ->> API: POST /auth/login
API ->> Au: authenticate(extToken)
Note right of Au: We may be able to verify locally, skipping this step
alt verify token
Au ->> EAu: verifyToken(extToken)
EAu -->> Au: extToken
end
Au ->> CredG: get(extToken)
CredG -->> Au: credential
Note right of Au: We can apply this to Producer as well
Au ->> CG: get(credential.userId)
CG -->> Au: consumer
Au ->> C: canLogin()
C -->> Au: true
Au ->> Au: generateAccessToken(credential)
Au -->> API: accessToken
API -->> A: accessToken
O aplicado em /consumers
é aplicado da mesma forma em /producers
.
Para editar os dados do utilizador podemos usar o endpoint /consumers/{consumerId}
com o método PUT
.
sequenceDiagram
actor A as Alice
participant API
participant CG as ConsumerGateway
participant C as Consumer
A ->> API: PUT /consumers/{consumerId}
API ->> CG: get(consumerId)
activate C
CG -->> API: consumer
Note right of API: we update the consumer
API ->> CG: update(consumer)
deactivate C
CG -->> API: updatedConsumer
API -->> A: updatedConsumer
Para remover a conta do utilizador podemos usar o endpoint /consumers/{consumerId}
com o método DELETE
.
sequenceDiagram
actor A as Alice
participant API
participant CG as ConsumerGateway
A ->> API: DELETE /consumers/{consumerId}
API ->> CG: delete(consumerId)
CG -->> API: deletedConsumer
API -->> A: deletedConsumer
Usando o mesmo endpoint /products/search
do rf-03 mas adicionando os campos específicos das categorias.
Ver notas da api para detalhes de utilização de filtros.
sequenceDiagram
actor A as Alice
participant API
participant PG as ProductGateway
A ->> API: GET /products/search?filter[category][name]=fruit
API ->> PG: find({category: {name: "fruit"}})
PG -->> API: productSpec[]
API -->> A: productSpec[]
Para obter o histórico de encomendas do utilizador podemos usar o endpoint /consumers/{consumerId}/orders
com o método GET
.
sequenceDiagram
actor A as Alice
participant API
participant CG as ConsumerGateway
participant OG as OrderGateway
A ->> API: GET /consumers/{consumerId}/orders
API ->> CG: get(consumerId)
CG -->> API: consumer
API ->> OG: findFromConsumer(consumer)
OG -->> API: order[]
API -->> A: order[]
Obter os dois ProductSpec
e iterar sobre as propriedades (e valores das categorias) de forma a saber quais diferem em valor.
Devemos definir se é possível comparar dois ProductSpec
de categorias diferentes.
Convém definir se podemos comparar ProducerProduct
com ProducerProduct
, possibilitando comparar o mesmo ProductSpec
de fornecedores diferentes, ProductSpec
com ProductSpec
, ou ambos.
- Com a primeira podemos comparar custos e impacto na comunidade
- A especificação da API seria um
GET
a/producers/{producerId}/products/{product1}/compare/{product2}
.- Isto parece estranho porque semânticamente estariamos a comparar produtos de um fornecedor com outro produto do mesmo fornecedor (e não outro produto qualquer)
- A especificação da API seria um
- Com a segunda só queremos saber das diferenças da especificação do produto, que nem inclui o preço
- A especificação da API seria um
GET
a/products/{product1}/compare/{product2}
.
- A especificação da API seria um
Podemos implementar ambos, mas acho que o primeiro é mais interessante.
sequenceDiagram
actor A as Alice
participant API
participant PSG as ProductSpecGateway
participant PS as ProductSpec
A ->> API: GET /products/{product1}/compare/{product2}
API ->> PSG: get(1)
PSG -->> API: product1
API ->> PSG: get(2)
PSG -->> API: product2
API ->> PS: product1.compare(product2)
PS -->> API: differences
API -->> A: differences
A especificação da API seria um POST
a /consumers/{consumerId}/orders
com o body a conter o cartId
.
sequenceDiagram
actor A as Alice
participant API
participant CaG as CartGateway
participant OF as OrderFactory
participant O as Order
participant C as Cart
participant OG as OrderGateway
participant CIG as CartItemGateway
participant OIF as OrderItemFactory
participant OI as OrderItem
participant OIG as OrderItemGateway
A ->> API: POST /consumers/{consumerId}/orders
API ->> CaG: get(consumerId)
activate C
CaG -->> API: cart
API ->> OF: createFromCart(cart)
OF ->> O: new()
OF ->> CIG: findFromCart(cart)
CIG -->> OF: cartItem[]
deactivate C
loop for each cartItem.product
OF ->> OIF: createFromCartItem(order, cartItem)
OIF ->> OI: new(order, cartItem)
OI -->> OIF: orderItem
OIF ->> OIG: insert(orderItem)
OIG -->> OIF: insertedOrderItem
OF ->> O: addItem(orderItem)
O -->> OF: addedOrderItem
end
OF ->> OG: insert(order)
OG -->> OF: insertedOrder
OF -->> API: order
API -->> A: order
Serviço externo: Stripe, usando o Stripe Checkout.
Começamos então por fazer um pedido POST
ao endpoint /consumers/{consumerId}/orders/{orderId}/checkout
irá criar um checkout session no Stripe e redirecionar o utilizador para o preenchimento de dados relativos ao pagamento, do lado do Stripe.
Após a introdução dos dados de pagamento, o Stripe irá confirmar o sucesso da operação ao submeter um pedido POST
a /payments/webhook
, aka webhook.
sequenceDiagram
actor A as Alice
participant API
participant CG as ConsumerGateway
participant OG as OrderGateway
participant O as Order
participant PG as PaymentGateway
participant S as Stripe
participant Su as Supplier
A ->> API: POST /consumers/{consumerId}/orders/{orderId}/checkout
API ->> CG: get(consumerId)
CG -->> API: consumer
API ->> OG: get(orderId)
OG -->> API: order
API ->> PG: createCheckoutSession(order)
PG ->> S: createCheckoutSession(order)
S -->> PG: checkoutSession
PG -->> API: checkoutSession
API -->> A: checkoutSession
Note right of A: Redirect to checkoutSession.url
Note right of A: Fill payment details and submit to external service
S ->> API: POST /payments/webhook
API ->> PG: handleWebhookEvent()
PG ->> OG: updateStatus(order, paid)
OG -->> PG: order
PG -->> API: order
Note right of O: Notify subscribers. See RF-14
API -->> S: 200 OK
A especificação da API seria um DELETE
a /consumers/{consumerId}/orders/{orderId}
.
Importante!!! Falta definir o prazo de cancelamento e outras regras.
Sugeria que o prazo de cancelamento fosse de 24h, mas que o utilizador pudesse cancelar a qualquer momento, desde que o produto ainda não tivesse sido enviado.
Também é possível definir um prazo de cancelamento para cada produto, definido pelo fornecedor, ou fornecedor-wide.
Este requisito será simplificado, i.e., o utilizador apenas pode cancelar encomendas na sua totalidade, não pode cancelar apenas um produto de uma encomenda.
sequenceDiagram
actor A as Alice
participant API
participant CG as ConsumerGateway
participant OG as OrderGateway
participant O as Order
participant PG as PaymentGateway
participant S as Stripe
A ->> API: DELETE /consumers/{consumerId}/orders/{orderId}
API ->> CG: get(consumerId)
CG -->> API: consumer
API ->> OG: get(orderId)
OG -->> API: order
Note right of API: Check if order is cancelable
API ->> O: canCancel()
O -->> API: true
Note right of API: Affect state, making order cancelled
API ->> O: cancel()
O -->> API: order
API ->> OG: update(order)
OG -->> API: updatedOrder
alt Order paid
API ->> PG: refund(updatedOrder.payment)
PG ->> S: refund(id)
Note right of S: Stripe will notify us through webhook
S ->> API: POST /payments/webhook
API ->> PG: handleWebhookEvent()
Note right of O: Notify subscribers. See RF-14
PG ->> O: setStatus(refunded)
O -->> PG: order
PG -->> API: order
API -->> S: 200 OK
end
API -->> A: updatedOrder
Interpretação: Notificação a cada ShipmentEvent
em vez de apenas uma notificação aquando da saída dos produtos de uma Order
.
Desta forma, não "existe" um endpoint para notificar o utilizador sobre a saída de produtos de uma encomenda, mas sim, um evento despoletado. Utilização do padrão Observer?
Primeiramente, necessitamos de persistir as notificações, podendo assim ser vistas novamente posteriormente, e não perdidas (ephemeral).
Após isso, devemos notificar as partes interessadas, os subscribers. Após persistir a notificação, podemos notificar os subscribers de forma assíncrona, i.e., em background, através de sockets (caso o subscriber estiver à escuta), e através do e-mail.
Notas de implementação: Uso do padrão Strategy Criar uma interface NotificationStrategy
e implementar SocketNotificationStrategy
assim como EmailNotificationStrategy
que, através dos SocketGateway
e EmailGateway
, respetivamente, notificam os subscribers.
sequenceDiagram
actor S as System
participant SE as ShipmentEvent
participant SEG as ShipmentEventGateway
participant N as Notification
participant NFG as NotificationGateway
%% TODO we need user
participant U as User
participant NMF as NotificationMessageFactory
# SocketGateway is NOT a data table gateway. It is a facade to the socket server
participant SG as SocketGateway
participant EG as EmailGateway
Note right of S: A System component makes a ShipmentEvent
S ->> SE: new({orderId, status, address, etc})
activate SE
SE -->> S: shipmentEvent
S ->> SEG: insert(shipmentEvent)
SEG -->> S: savedShipmentEvent
deactivate SE
# TODO confirmar que notificações são criadas doutras formas
S ->> N: new({shipmentEvent, subscriber})
activate N
N -->> S: notification
S ->> NFG: insert(notification)
NFG -->> S: savedNotification
Note right of N: Deliver notifications
loop for each subscriber in notification.subscribers
Note right of N: Push notification
N ->> SG: findSocket(subscriber)
alt Socket found
SG -->> N: socket
N ->> NMF: makeSocketNotificationMessage(shipmentEvent) # entity, type, emitter, etc
NMF -->> N: message
N ->> SG: send(socket, message)
SG -->> N: notificationSent
end
# TODO we will allow disabling types of notifications sent through email, so we must check we can send this type of notification
N ->> U: getNotificationPreferences()
U -->> N: notificationPreferences
Note right of N: Email notification
alt canSendEmail(notificationPreferences, shipmentEvent)
N ->> U: getEmail()
U -->> N: email
N ->> NMF: makeEmailNotificationMessage(shipmentEvent)
NMF -->> N: message
N ->> EG: send(email, message)
EG -->> N: notificationSent
end
end
deactivate N
Notar o diagrama de sequência acima. Visto que o ShipmentEvent
é um evento que ocorre quando a encomenda tem uma atualização no envio, podemos utilizar o mesmo evento para notificar o utilizador sobre a chegada iminente da encomenda.
Não sei
Um endpoint próprio para obter os dados tratados seria o mais eficiente. Depois, apenas seria necessário gravar a resposta num ficheiro (a API retorna sempre em JSON).
/consumers/{consumerId}/orders/export
sequenceDiagram
actor A as Alice
participant API as API
participant OG as OrderGateway
A ->> API: GET /consumers/{consumerId}/orders/export
Note right of API: We fetch the orders, loading the order items and the products, the Producer, and payment details
API ->> OG: findFromConsumer(consumerId)
OG -->> API: order[]
API -->> A: order[]
É mais simples separar em três partes: criação, gestão, e remoção.
A criação de uma unidade de produção é feita através de um endpoint próprio, que recebe os dados necessários para a criação da unidade de produção. Para tal, efetua-se POST
a /producers/{producerId}/units
sequenceDiagram
actor A as Alice
participant API as API
participant PU as ProductionUnit
participant PUG as ProductionUnitGateway
A ->> API: POST /producers/{producerId}/units
API ->> PU: new(producer, {name, description, etc})
activate PU
PU -->> API: unit
API ->> PUG: insert(unit)
PUG -->> API: savedUnit
deactivate PU
API -->> A: savedUnit
A gestão de uma unidade de produção é feita através de um endpoint próprio, que recebe os dados necessários para a gestão da unidade de produção. Para tal, efetua-se PUT
a /producers/{producerId}/units/{unitId}
sequenceDiagram
actor A as Alice
participant API as API
participant UG as UnitGateway
participant PU as ProductionUnit
A ->> API: PUT /producers/{producerId}/units/{unitId}
API ->> UG: get(unitId)
activate PU
UG -->> API: unit
Note right of API: We update the unit with the new data
API ->> UG: update(updatedUnit)
UG -->> API: savedUnit
deactivate PU
API -->> A: savedUnit
A remoção de uma unidade de produção é feita através de um endpoint próprio, que recebe os dados necessários para a remoção da unidade de produção. Para tal, efetua-se DELETE
a /producers/{producerId}/units/{unitId}
sequenceDiagram
actor A as Alice
participant API as API
participant PUG as ProductionUnitGateway
A ->> API: DELETE /producers/{producerId}/units/{unitId}
API ->> PUG: delete(unitId)
PUG -->> API: deletedUnit
API -->> A: deletedUnit
É mais simples separar em quatro partes: criação, gestão, remoção, e ligação a unidade de produção.
De forma semelhante ao RF-18, a criação de um produto é feita através de um endpoint próprio, que recebe os dados necessários para a criação do produto. Para tal, efetua-se POST
a /producers/{producerId}/products
sequenceDiagram
actor A as Alice
participant API as API
participant P as Product
participant PG as ProductGateway
A ->> API: POST /producers/{producerId}/products
API ->> P: new(producer, {name, description, etc})
activate P
P -->> API: product
API ->> PG: insert(product)
PG -->> API: savedProduct
deactivate P
API -->> A: savedProduct
De forma semelhante ao RF-18, a gestão de um produto é feita através de um endpoint próprio, que recebe os dados necessários para a gestão do produto. Para tal, efetua-se PUT
a /producers/{producerId}/products/{productId}
sequenceDiagram
actor A as Alice
participant API as API
participant PG as ProductGateway
participant P as Product
participant PP as ProductPrice
participant PPG as ProductPriceGateway
A ->> API: PUT /producers/{producerId}/products/{productId}
API ->> PG: get(productId)
activate P
PG -->> API: product
Note right of API: We update the product with the new data
API ->> P: update({name, description, etc})
alt price is changed
P ->> PP: new({product, currentPrice})
activate PP
PP -->> P: productPrice
P ->> PPG: create(productPrice)
PPG -->> P: savedProductPrice
deactivate PP
Note right of P: price = currentPrice
end
P -->> API: updatedProduct
API ->> PG: update(updatedProduct)
PG -->> API: savedProduct
deactivate P
API -->> A: savedProduct
De forma semelhante ao RF-18, a remoção de um produto é feita através de um endpoint próprio, que recebe os dados necessários para a remoção do produto. Para tal, efetua-se DELETE
a /producers/{producerId}/products/{productId}
sequenceDiagram
actor A as Alice
participant API as API
participant PG as ProductGateway
A ->> API: DELETE /producers/{producerId}/products/{productId}
API ->> PG: delete(productId)
PG -->> API: deletedProduct
API -->> A: deletedProduct
Para ligar um produto a uma unidade de produção é necessário que ambos tenham sido criados previamente. Depois, resta apenas adicionar o produto à coleção de produtos da unidade, através de PUT
a /producers/{producerId}/units/{unitId}/products/{productId}
sequenceDiagram
actor A as Alice
participant API as API
participant PUG as ProductionUnitGateway
participant PU as ProductionUnit
participant PG as ProductGateway
A ->> API: PUT /producers/{producerId}/units/{unitId}/products/{productId}
API ->> PUG: get(unitId)
activate PU
PUG -->> API: unit
API ->> PG: get(productId)
PG -->> API: product
Note right of API: We add the product to the unit
API ->> PU: addProduct(product)
PU -->> API: unit
API ->> PUG: update(updatedUnit)
PUG -->> API: updatedUnit
deactivate PU
API -->> A: updatedUnit
Iremos novamente separar em duas partes: visualização de unidade de produção, e visualização dos produtos da unidade de produção.
Apenas obtém os dados base da unidade de produção, através de GET
a /producers/{producerId}/units/{unitId}
sequenceDiagram
actor A as Alice
participant API as API
participant UG as UnitGateway
A ->> API: GET /producers/{producerId}/units/{unitId}
API ->> UG: get(unitId)
UG -->> API: unit
API -->> A: unit
De forma a obter a coleção de produtos da unidade de produção, efetua-se GET
a /producers/{producerId}/units/{unitId}/products
sequenceDiagram
actor A as Alice
participant API as API
participant UG as ProductionUnitGateway
participant PrPG as ProducerProductGateway
A ->> API: GET /producers/{producerId}/units/{unitId}/products
API ->> UG: get(unitId)
UG -->> API: unit
Note right of API: We get the products from the unit
API ->> PrPG: findFromProductionUnit()
PrPG -->> API: products
API -->> A: products
Bastante semelhante a RF-18.
Separando em três partes: criação, gestão, e remoção.
De forma semelhante ao RF-18, a criação de um veículo de transporte de produtos é feita através de um POST
a /producers/{producerId}/units/{unitId}/carriers
sequenceDiagram
actor A as Alice
participant API as API
participant CG as CarrierGateway
participant C as Carrier
A ->> API: POST /producers/{producerId}/units/{unitId}/carriers
Note right of API: We create the carrier
API -> C: new(producerId, unitId, {name, description, etc})
activate C
C -->> API: carrier
API ->> CG: insert(carrier)
CG -->> API: savedCarrier
deactivate C
API -->> A: savedCarrier
De forma semelhante ao RF-18, a edição de um carrier faz-se através de um PUT
a /producers/{producerId}/units/{unitId}/carriers/{carrierId}
sequenceDiagram
actor A as Alice
participant API
participant CG as CarrierGateway
A ->> API: PUT /producers/{producerId}/units/{unitIt}/carriers/{carrierId}
API ->> CG: get(carrierId)
CG -->> API: carrier
Note right of API: We edit the carrier
API ->> CG: update(carrier)
CG -->> API: savedCarrier
API -->> A: savedCarrier
De forma semelhante ao RF-18, a remoção de um carrier faz-se através de um DELETE
a /producers/{producerId}/units/{unitId}/carriers/{carrierId}
sequenceDiagram
actor A as Alice
participant API as API
participant CG as CarrierGateway
A ->> API: DELETE /producers/{producerId}/units/{unitId}/carriers/{carrierId}
API ->> CG: delete(carrierId)
CG -->> API: deletedCarrier
API -->> A: deletedCarrier
Podemos usar o mesmo evento ShipmentEvent
para notificar o utilizador sobre a criação de uma encomenda.
Assim, iriamos ter um ShipmentEvent
criado, cujo ShipmentStatus
seria encomenda criada
, porém pending
seria mais rigoroso.
Notar o diagrama de sequência acima. Visto que o
ShipmentEvent
é um evento que ocorre quando a encomenda tem uma atualização no envio, podemos utilizar o mesmo evento para notificar o utilizador sobre a chegada iminente da encomenda.
A visualização de uma encomenda de consumidor é feita através de um GET
a /consumers/{consumerId}/orders/{orderId}
sequenceDiagram
actor A as Alice
participant API as API
participant CG as ConsumerGateway
participant OG as OrderGateway
A ->> API: GET /consumers/{consumerId}/orders/{orderId}
API ->> CG: get(consumerId)
CG -->> API: consumer
API ->> OG: get(orderId)
OG -->> API: order
API -->> A: order
TODO
Basicamente o mesmo que o RF-14, mas com um ShipmentStatus
diferente.
Basicamente o mesmo que o RF-14, mas com um ShipmentStatus
diferente.
Não sei
De forma semelhante ao RF-18, a desativação de uma conta de utilizador faz-se através de um PUT
a /consumers/{consumerId}
, especificando o campo disabledOn
com a data a partir da qual a conta fica desativada ou null
, de forma a reativar a conta.
O mesmo se aplica aos produtores.
Poderíamos incluir um extra em que permite guardar o histórico de desativações e reativações, assim como quem despoletou a alteração e a razão.
sequenceDiagram
actor A as Alice
participant API
participant CG as ConsumerGateway
participant U as User
A ->> API: PUT /consumers/{consumerId}
API ->> CG: get(userId)
CG -->> API: consumer
Note right of API: We edit the disabledOn field
API ->> CG: update(consumer)
CG -->> API: savedConsumer
API -->> A: savedConsumer
Não sei