-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/initial dev #1
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Codecov Report
@@ Coverage Diff @@
## main #1 +/- ##
=======================================
Coverage ? 86.66%
=======================================
Files ? 5
Lines ? 195
Branches ? 40
=======================================
Hits ? 169
Misses ? 25
Partials ? 1 |
RobertRosca
force-pushed
the
feat/initial-dev
branch
3 times, most recently
from
August 4, 2023 10:53
47f021d
to
1286d8c
Compare
RobertRosca
force-pushed
the
feat/initial-dev
branch
2 times, most recently
from
August 4, 2023 11:48
45ac721
to
6abb489
Compare
RobertRosca
requested review from
matheuscteo and
CammilleCC
and removed request for
tmichela
November 7, 2023 12:35
@CammilleCC @matheuscteo ready for review. No major changes since you both last looked at it. |
…om _version _version might not exist depending on install method/context, __init__ imports handle this
Thank you Robert, LGTM. |
CammilleCC
approved these changes
Nov 15, 2023
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested in production, thus LGTM
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The application serves as a "Zulip Write Only Proxy," providing a web API to interact with Zulip services. It allows clients to send messages, upload images, and list topics within specific streams. The application also manages client authentication and implements a JSON-based repository for data persistence.
Software Choices
Project:
Architecture:
Description
The application (roughly) follows domain-driven design principles, creating a separation between different concerns and layers. DDD isn't really used in DA so I'll write a short description of the concepts.
Domain Driven Design Summary
Domain-driven design is a software development methodology that aims to create a separation between different concerns and layers. It is based on the idea that the domain (the problem being solved) should be the main focus of the software, and that the code should be organized around it. The domain is divided into different layers, each with its own responsibilities and concerns.
The layers used in this project are:
DDD is an 'onion' architecture with the domain at the core and everything else depending on the domain.
Domain Layer
The domain layer in this case is a single file
models.py
. It contains models for the scoped client (a Zulip client scoped to a single stream), and an admin client (an internal client which has the ability to create new scoped clients).In DDD the domain layer is at the center of the application, and is independent of the other layers. It should not import anything from the other layers, should not be aware of infrastructure concerns like persistence/storage, and should not be aware of presentation concerns like web APIs.
Infrastructure/Repository Layer
The infrastructure layer in this case is a single file
repository.py
. For simplicity right now this is just a JSON-based file storage, but it could be replaced with a database or other storage solution in the future.In DDD the infrastructure layer is responsible for data access and storage. It should be aware of domain concerns, since it returns domain objects, but should not be aware of the presentation layer.
Application/Service Layer
The application layer in this case is a single file
services.py
. It contains methods for creating, retrieving, and listing clients from an instance of a repository.In DDD the application layer is responsible for coordinating between the domain and data access layers. It should be aware of both the domain and the infrastructure layers, but should not be aware of the presentation layer.
Presentation/API Layer
The presentation layer in this case is the
main.py
file andcli.py
. It contains FastAPI endpoints and CLI commands for creating, retrieving, and listing clients. It also includes security measures like API key headers.In DDD the presentation layer is responsible for exposing the functionality to users, in this case through a web API and through a CLI interface. It should be aware of the application/service layer, but should not be aware of the domain or infrastructure layers.
Deployment
The production deployment involves multiple replicas of the service running in front of nginx. The nginx container acts as a load balancer, distributing requests between the replicas. This allows for continuous deployment with no downtime, since the replicas can be updated one at a time.
This is all defined in a docker compose file, but compose is not used for the deployment, instead docker stacks are used. Stacks are pretty much docker compose (they use compose file) but with some fancier features available, in this case the relevant features are in the
deploy
section ofdocker-compose.yml
:This configuration means that when the stack is updated, it will start up a new instance of the container, and wait for it to be running successfully. If the update worked then the old instance is removed, if an update fail it will leave the original container running.
Since the replicas are updated one at a time, and since nginx is configured to distribute requests between them, this allows for continuous deployment with no downtime caused by the update process.
'Normal' docker compose deployments would pull in the image, stop the old container, and start the new one, which would cause downtime. If the new container fails to start then the old one is already stopped so the service would be down. Stacks allow for a more controlled update process.
Todo
Required:
[ ] Simplify tests: A lot of tests are redundant, test scope should be limited to the responsibilities of a layer.already wrote the tests, might as well keep thempoe
commands, code style guide, commit style guide, formatting, type checking./me
endpoint for adminzulip-write-only-proxy:main
(or latest? I forget which tags the metadata extractor creates)Optional:
[ ] Docs? Overkill since this is so simple, but also very easy to set up. Repo is public and this kind of stream-scoped client might be useful to others using Zulip bots so maybe docs are worth including.