{"id":17342558,"url":"https://github.com/ak1m1tsu/exile","last_synced_at":"2026-04-10T02:58:05.125Z","repository":{"id":194538929,"uuid":"691040316","full_name":"ak1m1tsu/exile","owner":"ak1m1tsu","description":"A service that adds people whose age, gender and nationality are taken from external APIs.","archived":false,"fork":false,"pushed_at":"2023-11-18T08:47:39.000Z","size":274,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-25T13:10:49.937Z","etag":null,"topics":["docker","docker-compose","go","golang","graphql","kafka","postgresql","redis"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ak1m1tsu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-13T11:38:53.000Z","updated_at":"2023-10-03T05:58:46.000Z","dependencies_parsed_at":"2023-11-18T09:29:04.881Z","dependency_job_id":"43977002-a186-4724-98b8-56a66929c7d9","html_url":"https://github.com/ak1m1tsu/exile","commit_stats":null,"previous_names":["romankravchuk/effective-mobile-test-task","insan1a/exile"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ak1m1tsu/exile","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ak1m1tsu%2Fexile","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ak1m1tsu%2Fexile/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ak1m1tsu%2Fexile/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ak1m1tsu%2Fexile/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ak1m1tsu","download_url":"https://codeload.github.com/ak1m1tsu/exile/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ak1m1tsu%2Fexile/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29461367,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-14T22:42:09.113Z","status":"ssl_error","status_checked_at":"2026-02-14T22:42:05.053Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","docker-compose","go","golang","graphql","kafka","postgresql","redis"],"created_at":"2024-10-15T16:06:14.067Z","updated_at":"2026-02-15T00:02:34.628Z","avatar_url":"https://github.com/ak1m1tsu.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Exile\n\n## Technologies\n\n- Redis (cache)\n- PostgreSQL (database)\n- Apache Kafka (communication between services)\n- GraphQL (API gateway endpoints)\n- Docker (deploy services, database, etc.)\n- External APIs\n    - https://api.agify.io\n    - https://api.genderize.io\n    - https://api.nationalize.io\n\n## Design diagram\n\n![](./assets/exile-system-design.svg)\n\n## Services\n\n### Person service\n\nA service that receives a stream of full name, enriches the response with the most probable age, gender and nationality using open APIs and stores the data in the database.\n\n### API Gateway\n\n### Get list of persons\n\n```shell\ncurl http://localhost:5555/person\n```\n\n**Response**\n\n```json\n{\n  \"status\": \"OK\",\n  \"people\": [\n    {\n      \"ID\": \"05dd6483-1938-4d8b-9a45-7f61a69ad377\",\n      \"Name\": \"Ivan\",\n      \"Surname\": \"Ivanov\",\n      \"Patronymic\": \"\",\n      \"Age\": 54,\n      \"Gender\": \"male\",\n      \"Nationality\": \"HR\",\n      \"IsDeleted\": false\n    }\n  ]\n}\n```\n\n### Get person\n\n```shell\ncurl http://localhost:5555/person/\u003cid\u003e\n```\n\n**Response**\n\n```json\n{\n  \"status\": \"OK\",\n  \"person\": {\n    \"ID\": \"05dd6483-1938-4d8b-9a45-7f61a69ad377\",\n    \"Name\": \"Ivan\",\n    \"Surname\": \"Ivanov\",\n    \"Patronymic\": \"\",\n    \"Age\": 54,\n    \"Gender\": \"male\",\n    \"Nationality\": \"HR\",\n    \"IsDeleted\": false\n  }\n}\n```\n\n### Create new person\n\n```shell\ncurl -X POST --data '{\"name\":\"Ivan\", \"surname\":\"Ivanov\", \"patronymic\":\"Ivanovich\"}' http://localhost:5555/person\n```\n\n**Response**\n\n```json\n{\n    \"status\": \"OK\",\n    \"person\": {\n        \"ID\": \"05dd6483-1938-4d8b-9a45-7f61a69ad377\",\n        \"Name\": \"Ivan\",\n        \"Surname\": \"Ivanov\",\n        \"Patronymic\": \"Ivanovich\",\n        \"Age\": 54,\n        \"Gender\": \"male\",\n        \"Nationality\": \"HR\",\n        \"IsDeleted\": false\n    }\n}\n```\n\n### Update a person\n\n```shell\ncurl -X PATCH --data '{\"name\":\"Roman\",\"surname\":\"Kravchuk\",\"age\":21}' http://localhost:5555/person/\u003cid\u003e\n```\n\n**Response**\n\n```json\n{\n  \"status\": \"OK\",\n  \"person\": {\n    \"ID\": \"05dd6483-1938-4d8b-9a45-7f61a69ad377\",\n    \"Name\": \"Roman\",\n    \"Surname\": \"Kravchuk\",\n    \"Patronymic\": \"Ivanovich\",\n    \"Age\": 21,\n    \"Gender\": \"male\",\n    \"Nationality\": \"HR\",\n    \"IsDeleted\": false\n  }\n}\n```\n\n### Delete a person\n\n```shell\ncurl -X DELETE http://localhost:5555/person/\u003cid\u003e\n```\n\n**Response**\n\n```json\n{\n    \"status\": \"OK\"\n}\n```\n\n### GraphQL endpoints\n\n```shell\ncurl -X POST http://localhost:5555/person/graphql\n```\n\n## How to run?\n\nCreate config file `.env`:\n\n```shell\n# Postgres configuration\nPOSTGRES_USER=postgres\nPOSTGRES_PASSWORD=postgrespwd\nPOSTGRES_DB=emdb\n# Kafka configuration\nKAFKA_BROKER_ID=1\nKAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT\nKAFKA_ADVERTISED_LISTENERS=PLAINTEXT://broker:29092,PLAINTEXT_HOST://broker:9092\nKAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1\nKAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0\nKAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1\nKAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1\nKAFKA_PROCESS_ROLES=broker,controller\nKAFKA_NODE_ID=1\nKAFKA_CONTROLLER_QUORUM_VOTERS=1@broker:29093\nKAFKA_LISTENERS=PLAINTEXT://broker:29092,CONTROLLER://broker:29093,PLAINTEXT_HOST://0.0.0.0:9092\nKAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT\nKAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER\nKAFKA_LOG_DIRS=/tmp/kraft-combined-logs\nCLUSTER_ID=MkU3OEVBNTcwNTJENDM2Qk\n# Redis configuration\nREDIS_PASSWORD=redispwd\n# Service configuration\nSERVICE_ENV=development\nSERVICE_KAFKA_GROUP_ID=\"exile\"\nSERVICE_KAFKA_BOOTSTRAP_SERVERS=\"broker:9092\"\nSERVICE_KAFKA_AUTO_OFFSET_RESET=earliest\nSERVICE_KAFKA_PRODUCER_TOPIC=FIO_FAILED\nSERVICE_KAFKA_CONSUMER_TOPICS=FIO\nSERVICE_KAFKA_TIMEOUT=100ms\n# API configuration\nAPI_ENV=development\nAPI_PORT=5555\nAPI_IDLE_TIMEOUT=30s\nAPI_READ_TIMEOUT=5s\nAPI_WRITE_TIMEOUT=5s\nAPI_KAFKA_BOOTSTRAP_SERVERS=\"broker:9092\"\nAPI_KAFKA_PRODUCER_TOPIC=FIO\n# URLs\nDATABASE_URL=\"postgres://postgres:postgrespwd@postgres:5432/emdb?sslmode=disable\"\nCACHE_URL=\"redis://default:redispwd@redis:6379/0\"\n```\n\nNext, use this commands to start containers and create Kafka topics\n\n```shell\nmake up \u0026\u0026 make seedkafka\n```\n\n## `Makefile` commands\n\n| Команда \t| Описание \t|\n|---\t|---\t|\n| `make up` \t| Запускает скрипт `dockerup.sh`, который билдит все докер контейнеры. \t|\n| `make down` \t| Останавливает запущенные контейнеры \t|\n| `make gen` \t| Генерирует моки для интерфейсов, используя [mockery](https://github.com/vektra/mockery) \t|\n| `make seedkafka` \t| Создает топики **FIO** и **FIO_FAILED**  \t|\n| `make tests` \t| Запускает unit-тесты \t|\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fak1m1tsu%2Fexile","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fak1m1tsu%2Fexile","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fak1m1tsu%2Fexile/lists"}