{"id":27652952,"url":"https://github.com/jorgermduarte/hexagonal-weather-app-example","last_synced_at":"2026-05-05T12:31:14.811Z","repository":{"id":289004338,"uuid":"969807195","full_name":"jorgermduarte/hexagonal-weather-app-example","owner":"jorgermduarte","description":"A modular and extensible example API built with Node.js, TypeScript, and Hexagonal Architecture (Ports \u0026 Adapters).","archived":false,"fork":false,"pushed_at":"2025-04-21T00:59:09.000Z","size":38,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-07T04:07:23.519Z","etag":null,"topics":["clean-architecture","eslint","example-project","hexagonal-architecture","husky","nodejs","ports-and-adapters","rest-api","typescript","weather-api"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/jorgermduarte.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,"zenodo":null}},"created_at":"2025-04-21T00:54:16.000Z","updated_at":"2025-04-21T01:03:39.000Z","dependencies_parsed_at":"2025-04-24T05:01:40.856Z","dependency_job_id":null,"html_url":"https://github.com/jorgermduarte/hexagonal-weather-app-example","commit_stats":null,"previous_names":["jorgermduarte/hexagonal-weather-app"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jorgermduarte/hexagonal-weather-app-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jorgermduarte%2Fhexagonal-weather-app-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jorgermduarte%2Fhexagonal-weather-app-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jorgermduarte%2Fhexagonal-weather-app-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jorgermduarte%2Fhexagonal-weather-app-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jorgermduarte","download_url":"https://codeload.github.com/jorgermduarte/hexagonal-weather-app-example/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jorgermduarte%2Fhexagonal-weather-app-example/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32649504,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-05T11:29:49.557Z","status":"ssl_error","status_checked_at":"2026-05-05T11:29:48.587Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["clean-architecture","eslint","example-project","hexagonal-architecture","husky","nodejs","ports-and-adapters","rest-api","typescript","weather-api"],"created_at":"2025-04-24T05:01:38.094Z","updated_at":"2026-05-05T12:31:14.777Z","avatar_url":"https://github.com/jorgermduarte.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🌦️ Hexagonal Weather App (Node.js + TypeScript)\n\nThis project is a simple API that retrieves current weather data from external services (MetaWeather and wttr.in), using **Hexagonal Architecture** (Ports \u0026 Adapters) in a modular and scalable way.\n\n---\n\n## 🧱 What is Hexagonal Architecture?\n\nHexagonal Architecture (also known as Ports and Adapters) separates the business logic from external concerns like APIs, databases, or UI.  \n- The **core logic** interacts only through **ports (interfaces)**.\n- **Adapters** plug into these ports to handle communication with the outside world.\n\nThis makes the application:\n- More testable\n- Easier to maintain\n- Completely decoupled from frameworks or infrastructure\n\n---\n\n## 📁 Project Structure\n\n```\nsrc/\n├── domain/                  # Business entities and rules (pure logic)\n│   └── Weather.ts\n├── port/                    # Interfaces (ports)\n│   ├── in/                  # Input ports (what the app can do)\n│   └── out/                 # Output ports (what the app needs)\n├── application/             # Use cases (application services)\n│   └── get-weather.service.ts\n├── adapter/\n│   ├── in/                  # Inbound adapters (HTTP controllers)\n│   │   └── http/\n│   └── out/                 # Outbound adapters (API clients)\n│       └── weatherapi/\n├── config/                  # Dependency injection\n│   └── injector.ts\n├── main.ts                  # Entry point\n```\n\n\u003c!--\n---\n\n## 🔌 What are Injectors?\n\nInjectors are the part of the system responsible for wiring everything together.\n\nIn `config/injector.ts`, we decide **which implementation** of a port to use based on environment variables. For example:\n\n- `WeatherServiceOutputPort` (interface) can be implemented by:\n  - `MetaWeatherOutputAdapter`\n  - `WttrInOutputAdapter`\n\nYou can switch providers simply by setting the environment variable:\n\n```\nWEATHER_PROVIDER=wttr\n\n```\n---\n\n --\u003e\n\n\n\n---\n\n## ❓ Common Questions People Have\n\n### 🤔 What's the difference between `domain` and `application`?\n\n\u003e \"Isn't the business logic in the use case the same as the domain?\"\n\nNot quite. Here's how they differ:\n\n#### 🧠 `domain/` – Pure Business Logic\n\n- Holds **entities**, **value objects**, and **rules** specific to the business.\n- It has **no awareness** of external tools like HTTP, databases, or APIs.\n- Think of it as the core **vocabulary and rules** of your system.\n\n\u003e The domain is what your business **is**, not what it **does**.\n\n#### ⚙️ `application/` – Use Cases\n\n- Coordinates **actions** using the domain.\n- Contains orchestration logic: *how* to do something.\n- Talks to the domain and delegates work to ports/adapters.\n\n\u003e The application layer decides how to use the domain to solve a specific problem.\n\n---\n\n### 🗄️ What if I had a database? Are entities part of the domain?\n\n\u003e \"If I use a database, are the entities defined in the domain layer?\"\n\nYes, **entities absolutely belong in the domain**.\n\nBut here's the distinction:\n\n- The **domain entity** (e.g. `Weather`) defines business **rules**, behavior, and meaning.\n- The **database** is just a storage mechanism — it persists the entity, but isn't part of the domain itself.\n- You may have **DTOs** or **ORM models** that mirror the entity structure, but they're infrastructure concerns.\n\n**The domain owns the rules. The adapter handles persistence.**\n\n\u003e Think of it like this:\n\u003e Domain entity: “I know what a `Weather` means.”\n\u003e Adapter: “I know how to save and load it.”\n\nSo yes, entities live in the `domain/` folder, and any database-specific representation (like a Prisma/TypeORM model) lives in an adapter.\n---\n\n## 🚀 Getting Started\n\n```bash\nnpm install\nnpm start\n```\n\nOptionally, create a `.env` file:\n\n```\nWEATHER_PROVIDER=wttr\n```\n\n---\n\n## 🧪 Testing with VS Code\n\nInstall the **REST Client** extension and use the following in a `.http` file:\n\n```http\nGET http://localhost:3000/api/weather?city=Lisbon\n```\n\n---\n\n## ✅ Tech Stack\n\n- Node.js\n- TypeScript\n- Express\n- ts-node\n- eslint (Google style)\n\n---\n\nDesigned for clarity, modularity, and scalability ✨\n\n---\n\n## ✅ Pros and ❌ Cons of Using Hexagonal Architecture\n\n### ✅ Pros\n\n- **Separation of Concerns**: Keeps business logic isolated from infrastructure.\n- **Testability**: Core logic can be tested without involving databases or APIs.\n- **Flexibility**: Easily swap out external services (e.g. different weather providers).\n- **Scalability**: Supports growing complexity by clean separation of responsibilities.\n- **Maintainability**: Clear boundaries make it easier for teams to navigate and change.\n\n### ❌ Cons\n\n- **Initial Overhead**: Requires more structure and boilerplate up front.\n- **Learning Curve**: Developers unfamiliar with the architecture may find it harder to get started.\n- **Overkill for Small Projects**: Can be too heavy for very simple apps or scripts.\n- **Verbosity**: More files, interfaces, and layers than a traditional MVC setup.\n\n\u003e Summary: Hexagonal architecture is a great fit when you want **long-term scalability, clarity, and testability**, but it may feel verbose or complex if you're just prototyping something small.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjorgermduarte%2Fhexagonal-weather-app-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjorgermduarte%2Fhexagonal-weather-app-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjorgermduarte%2Fhexagonal-weather-app-example/lists"}