Invoice management system, microservice architecture.
The services implement Vertical Slice Architecture, YARP reverse proxy, RabbitMq message broker, request-response pattern, minimal api,
PFD file generation, miniIO object storage, integration and unit tests.
π· Frameworks, Libraries and Technologies
ποΈ List of microservices
-
Gateway service: Single entry point to the application.
-
Identity service: User authentication.
-
Company service: Manages interactions with companies.
-
Invoice service: Manages interaction with the invoices.
-
FileGenerator service: Generates PDF files.
-
Storage service: Stores invoices in MinIO storage.
ποΈ Microservices diagram
π³ List of docker containers
-
gateway.api - reverse proxy gateway container.
-
rabbitmq - message broker container.
-
minio - object storage for storage service.
-
storage.api - asp.net app container for storage service.
-
file-generator.api - asp.net app container for file generator service.
-
identity.api - asp.net app container for identity service.
-
identity.api.database - postgresql database container for identity service.
-
company.api - asp.net app container for company service.
-
company.api.database - postgresql database container for company service.
-
invoice.api - asp.net app container for invoice service.
-
invoice.api.database - postgresql database container for invoice service.
Allows you to run all integration and unit tests.
> dotnet test # donet SKD is required
π How to run the server
-
Build and start Docker images based on the configuration defined in the docker-compose.yml
> make up # docker-compose up --build
-
Stop and remove containers
> make down # docker-compose down
π¨οΈ Swagger UI documentation (local access)
-
Identity service
http://localhost:8000/swagger/index.html
-
Company service
http://localhost:8001/swagger/index.html
-
Invoice service
http://localhost:8002/swagger/index.html
-
Storage service
http://localhost:8080/swagger/index.html
π§ Implementation features
POST
/auth/register
(allows you to register)
name |
type |
data type |
email |
required |
string |
password |
required |
string |
http code |
content-type |
response |
201 |
application/json |
{"userId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "email": "string"} |
400 |
application/json |
array |
409 |
application/json |
string |
POST
/auth/login
(allows you to login, issues accessToken and refreshToken)
name |
type |
data type |
email |
required |
string |
password |
required |
string |
http code |
content-type |
response |
200 |
application/json |
{"accessToken": "string", "refreshToken": "string", "refreshTokenExpires": "2024-04-21T17:42:12.146Z", "accessTokenType": "string"} |
400 |
application/json |
array |
404 |
application/json |
string |
POST
/auth/refresh
(allows to refresh access and refresh tokens)
name |
type |
data type |
"refreshToken" |
required |
string |
http code |
content-type |
response |
200 |
application/json |
{"accessToken": "string", "refreshToken": "string", "refreshTokenExpires": "2024-04-21T17:43:47.494Z", "accessTokenType": "string"} |
400 |
application/json |
array |
401 |
application/json |
string |
POST
/auth/logout
(allows to logout and deactivates refresh tokens)
name |
type |
data type |
"refreshToken" |
required |
string |
http code |
content-type |
response |
204 |
application/json |
NoContent |
400 |
application/json |
array |
401 |
application/json |
string |
Functionality that allows to manage and interact with companies
Create new companies (πToken required)
POST
/company
(allows to create new companies ποΈ[token required])
name |
type |
data type |
"name" |
required |
string |
"taxNumber" |
required |
string |
"city" |
required |
string |
"street" |
required |
string |
"houseNumber" |
required |
string |
"postalCode" |
required |
string |
http code |
content-type |
response |
201 |
application/json |
{"companyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "string", "taxNumber": "string", "city": "string", "street": "string", "houseNumber": "string", "postalCode": "string"} |
400 |
application/json |
array |
401 |
application/json |
string |
403 |
application/json |
string |
Update your companies (πToken required)
PUT
/company
(allows to update your companies ποΈ[token required])
name |
type |
data type |
"companyId" |
required |
uuid |
"name" |
not required |
string |
"taxNumber" |
not required |
string |
"city" |
not required |
string |
"street" |
not required |
string |
"houseNumber" |
not required |
string |
"postalCode" |
not required |
string |
http code |
content-type |
response |
200 |
application/json |
{"companyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "string", "taxNumber": "string", "city": "string", "street": "string", "houseNumber": "string", "postalCode": "string"} |
400 |
application/json |
array |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
Delete your companies (πToken required)
DELETE
/company/{ id:uuid }
(allows to delete your companies ποΈ[token required])
http code |
content-type |
response |
204 |
application/json |
NoContent |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
Get all your companies (πToken required)
GET
/company
(allows you to get all your companies ποΈ[token required])
name |
type |
data type |
PageNumber |
not required |
int32 |
PageSize |
not required |
int32 |
http code |
content-type |
response |
200 |
application/json |
{"items": [ { "companyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "string", "taxNumber": "string", "city": "string", "street": "string", "houseNumber": "string", "postalCode": "string" } ], "pageNumber": 0, "totalPages": 0, "totalItemsCount": 0 } |
401 |
application/json |
string |
403 |
application/json |
string |
Get one your company (πToken required)
GET
/company/{ id:uuid }
(allows you to get one your company ποΈ[token required])
http code |
content-type |
response |
200 |
application/json |
{"companyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "string", "taxNumber": "string", "city": "string", "street": "string", "houseNumber": "string", "postalCode": "string"} |
401 |
application/json |
string |
403 |
application/json |
string |
Functionality that allows to manage and interact with invoices
Create invoices (πToken required)
POST
/invoice
(allows to create new invoices ποΈ[token required])
name |
type |
data type |
"sellerCompanyId" |
required |
uuid |
"buyerCompanyId" |
required |
uuid |
"termsOfPayment" |
required |
int |
"paymentType" |
required |
string |
"status" |
required |
string |
http code |
content-type |
response |
201 |
application/json |
{ "invoiceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "sellerCompanyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "buyerCompanyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "number": "string", "totalNetPrice": 0, "totalGrossPrice": 0, "termsOfPayment": 0, "paymentType": "string", "status": "string", "itemsId": ["3fa85f64-5717-4562-b3fc-2c963f66afa6"] } |
400 |
application/json |
array |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
Update your unfinished invoices (πToken required)
PUT
/invoice
(allows to update your invoices ποΈ[token required])
name |
type |
data type |
"invoiceId" |
required |
uuid |
"status" |
required |
string |
http code |
content-type |
response |
200 |
application/json |
{ "invoiceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "sellerCompanyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "buyerCompanyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "number": "string", "totalNetPrice": 0, "totalGrossPrice": 0, "termsOfPayment": 0, "paymentType": "string", "status": "string", "itemsId": ["3fa85f64-5717-4562-b3fc-2c963f66afa6"] } |
400 |
application/json |
array |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
Delete your unfinished invoices (πToken required)
DELETE
/invoice/{ id:uuid }
(allows to delete your invoices ποΈ[token required])
http code |
content-type |
response |
204 |
application/json |
NoContent |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
Finalize your unfinished invoices (πToken required)
PATCH
/invoice/lock/{ id:uuid }
(allows to finalize your invoices ποΈ[token required])
http code |
content-type |
response |
200 |
application/json |
string |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
Get all your invoices (πToken required)
GET
/invoice
(allows you to get all your invoices ποΈ[token required])
name |
type |
data type |
PageNumber |
not required |
int32 |
PageSize |
not required |
int32 |
http code |
content-type |
response |
200 |
application/json |
[ { "invoiceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "sellerCompanyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "buyerCompanyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "number": "string", "totalNetPrice": 0, "totalGrossPrice": 0, "termsOfPayment": 0, "paymentType": "string", "status": "string", "itemsId": [ "3fa85f64-5717-4562-b3fc-2c963f66afa6" ] } ] |
401 |
application/json |
string |
403 |
application/json |
string |
Get one your invoice (πToken required)
GET
/invoice/{ id:uuid }
(allows you to get one your company ποΈ[token required])
http code |
content-type |
response |
200 |
application/json |
[ { "invoiceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "sellerCompanyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "buyerCompanyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "number": "string", "totalNetPrice": 0, "totalGrossPrice": 0, "termsOfPayment": 0, "paymentType": "string", "status": "string", "itemsId": [ "3fa85f64-5717-4562-b3fc-2c963f66afa6" ] } ] |
401 |
application/json |
string |
403 |
application/json |
string |
Functionality that allows to manage and interact with invoice items
Create new items on your unfinished invoice (πToken required)
POST
/item
(allows to create new items ποΈ[token required])
name |
type |
data type |
"invoiceId" |
required |
uuid |
"name" |
required |
string |
"amount" |
required |
int |
"unit" |
required |
string |
"vat" |
required |
string |
"netPrice" |
required |
decimal |
http code |
content-type |
response |
201 |
application/json |
{ "invoiceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "itemId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "string", "amount": 0, "unit": "string", "vat": "string", "netPrice": 0, "sumNetPrice": 0, "sumGrossPrice": 0 } |
400 |
application/json |
array |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
Update items on your unfinished invoice (πToken required)
PUT
/item
(allows to update your items ποΈ[token required])
name |
type |
data type |
"itemId" |
required |
uuid |
"name" |
not required |
string |
"amount" |
not required |
int |
"netPrice" |
not required |
decimal |
http code |
content-type |
response |
200 |
application/json |
{ "invoiceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "itemId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "string", "amount": 0, "unit": "string", "vat": "string", "netPrice": 0, "sumNetPrice": 0, "sumGrossPrice": 0 } |
400 |
application/json |
array |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
Delete items on your unfinished invoice (πToken required)
DELETE
/item/{ id:uuid }
(allows to delete your items ποΈ[token required])
http code |
content-type |
response |
204 |
application/json |
NoContent |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
Get all items by invoice (πToken required)
GET
/item/all-by-invoice/{ id:uuid }
(allows you to get items by invoice ποΈ[token required])
http code |
content-type |
response |
200 |
application/json |
[ { "invoiceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "itemId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "string", "amount": 0, "unit": "string", "vat": "string", "netPrice": 0, "sumNetPrice": 0, "sumGrossPrice": 0 } ] |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
Get one item by id (πToken required)
GET
/item/{ id:uuid }
(allows you to get one item ποΈ[token required])
http code |
content-type |
response |
200 |
application/json |
[ { "invoiceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "itemId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "string", "amount": 0, "unit": "string", "vat": "string", "netPrice": 0, "sumNetPrice": 0, "sumGrossPrice": 0 } ] |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
PDF file is created automatically when the invoice is finalized
Get names of all your PDF files (πToken required)
GET
/storage
(allows to get names of all your PDF files ποΈ[token required])
http code |
content-type |
response |
200 |
application/json |
[ "string", "string", ] |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |
Download your PDF file by name (πToken required)
GET
/storage/{ fileName:string }
(allows to download your PDF file ποΈ[token required])
http code |
content-type |
response |
200 |
application/pdf |
PDFfile |
401 |
application/json |
string |
403 |
application/json |
string |
404 |
application/json |
string |