From 69ab1233e6e571d8d5c72b37d2db331851ea59e2 Mon Sep 17 00:00:00 2001 From: Paul de Raaij Date: Sun, 26 Jul 2026 07:02:30 -0400 Subject: [PATCH] feat: add ClassifiedAd domain entity --- .agent/current-plan.md | 35 +++++++++++++++++ .gitignore | 5 +++ AGENTS.md | 33 ++++++++++++++++ domain/__init__.py | 1 + domain/marketplace/__init__.py | 1 + domain/marketplace/classified_ad.py | 12 ++++++ pyproject.toml | 11 ++++++ tests/test_classified_ad.py | 61 +++++++++++++++++++++++++++++ 8 files changed, 159 insertions(+) create mode 100644 .agent/current-plan.md create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 domain/__init__.py create mode 100644 domain/marketplace/__init__.py create mode 100644 domain/marketplace/classified_ad.py create mode 100644 pyproject.toml create mode 100644 tests/test_classified_ad.py diff --git a/.agent/current-plan.md b/.agent/current-plan.md new file mode 100644 index 0000000..ef75c1b --- /dev/null +++ b/.agent/current-plan.md @@ -0,0 +1,35 @@ +# Plan: ClassifiedAd Domain Entity + +## Status: COMPLETED + +## Objective +Create the first Domain Entity representing a ClassifiedAd with the specified attributes. + +## Decisions Made +- **Location**: `domain/marketplace/classified_ad.py` +- **Class style**: `@dataclass(frozen=True)` for immutability +- **Validation**: No validation, just hold data +- **Tests**: Yes, create unit tests + +## Entity Structure +```python +@dataclass(frozen=True) +class ClassifiedAd: + id: UUID + _ownerId: UUID + title: str + text: str + _price: Decimal +``` + +## Files Created +- `domain/__init__.py` +- `domain/marketplace/__init__.py` +- `domain/marketplace/classified_ad.py` +- `tests/test_classified_ad.py` +- `pyproject.toml` + +## Test Results +- `test_classified_ad_creation` - PASSED +- `test_classified_ad_is_immutable` - PASSED +- `test_classified_ad_equality` - PASSED \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac78f51 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +env/ +__pycache__/ +*.pyc +.pytest_cache/ +*.egg-info/ \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..39cef4a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,33 @@ +# AGENTS.md + +## Project Context + +This is a training project to learn functional programming in Python. Both Python and functional programming are relatively new. The learning approach uses an example project based on "Hands-on Domain Driven Design with .NET Core". + +## Collaboration Principles + +### Communication Style +- Be honest and objective. Evaluate all suggestions, ideas, and feedback on their technical merits. +- Do not be overly complementary or sycophantic. +- If something does not align with best practices or could be improved, say so directly and constructively. +- Technical accuracy and project quality take precedence over being agreeable. + +### Command and Code Style +- Prefer simple commands over complex ones. +- Default to simple, single-purpose commands. +- Avoid one-liners that sacrifice clarity for brevity. + +### Workflow +- We build production code together. +- User handles implementation details; AI guides architecture and catches complexity early. +- Challenge assumptions and ideas when it's useful. Be critical, not agreeable. + +## Core Workflow: Research → Plan → Implement → Validate +Start every feature with: "Let me research the codebase and create a plan before implementing." + +Research - Understand existing patterns and architecture +Plan - Propose approach and verify with you +Implement - Build with tests and error handling +Validate - ALWAYS run formatters, linters, and tests after implementation +Whenever working on a feature or issue, let's always come up with a plan first, then save it to a file called /.agent/current-plan.md, before getting started with code changes. Update this file as the work progresses. +Let's use pure functions where possible to improve readability and testing. \ No newline at end of file diff --git a/domain/__init__.py b/domain/__init__.py new file mode 100644 index 0000000..2dc0adf --- /dev/null +++ b/domain/__init__.py @@ -0,0 +1 @@ +from .marketplace import classified_ad \ No newline at end of file diff --git a/domain/marketplace/__init__.py b/domain/marketplace/__init__.py new file mode 100644 index 0000000..b39d335 --- /dev/null +++ b/domain/marketplace/__init__.py @@ -0,0 +1 @@ +from .classified_ad import ClassifiedAd \ No newline at end of file diff --git a/domain/marketplace/classified_ad.py b/domain/marketplace/classified_ad.py new file mode 100644 index 0000000..663d3d6 --- /dev/null +++ b/domain/marketplace/classified_ad.py @@ -0,0 +1,12 @@ +from dataclasses import dataclass +from decimal import Decimal +from uuid import UUID + + +@dataclass(frozen=True) +class ClassifiedAd: + id: UUID + _ownerId: UUID + title: str + text: str + _price: Decimal \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..41c5525 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,11 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.backends._legacy:_Backend" + +[project] +name = "python-fun" +version = "0.1.0" +requires-python = ">=3.12" + +[tool.pytest.ini_options] +pythonpath = ["."] \ No newline at end of file diff --git a/tests/test_classified_ad.py b/tests/test_classified_ad.py new file mode 100644 index 0000000..6433bfc --- /dev/null +++ b/tests/test_classified_ad.py @@ -0,0 +1,61 @@ +from decimal import Decimal +from uuid import UUID, uuid4 + +from domain.marketplace.classified_ad import ClassifiedAd + + +def test_classified_ad_creation(): + ad_id = uuid4() + owner_id = uuid4() + + ad = ClassifiedAd( + id=ad_id, + _ownerId=owner_id, + title="Test Title", + text="Test text content", + _price=Decimal("99.99"), + ) + + assert ad.id == ad_id + assert ad._ownerId == owner_id + assert ad.title == "Test Title" + assert ad.text == "Test text content" + assert ad._price == Decimal("99.99") + + +def test_classified_ad_is_immutable(): + ad = ClassifiedAd( + id=uuid4(), + _ownerId=uuid4(), + title="Title", + text="Text", + _price=Decimal("10.00"), + ) + + try: + ad.title = "New Title" # type: ignore + assert False, "Should have raised FrozenInstanceError" + except AttributeError: + pass + + +def test_classified_ad_equality(): + ad_id = uuid4() + owner_id = uuid4() + + ad1 = ClassifiedAd( + id=ad_id, + _ownerId=owner_id, + title="Title", + text="Text", + _price=Decimal("10.00"), + ) + ad2 = ClassifiedAd( + id=ad_id, + _ownerId=owner_id, + title="Title", + text="Text", + _price=Decimal("10.00"), + ) + + assert ad1 == ad2 \ No newline at end of file