{"id":24543725,"url":"https://github.com/mushthak/nova-blend-salon-case-study","last_synced_at":"2025-03-16T08:09:05.401Z","repository":{"id":218934438,"uuid":"743989968","full_name":"mushthak/nova-blend-salon-case-study","owner":"mushthak","description":"Case study of Swift TDD approach using modular, composable, clean architecture, solid principles and CI-CD","archived":false,"fork":false,"pushed_at":"2024-11-27T10:41:21.000Z","size":1777,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-22T20:14:50.801Z","etag":null,"topics":["ci-cd","clean-architecture","composition","modular","tdd"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mushthak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2024-01-16T12:12:56.000Z","updated_at":"2024-12-16T10:47:57.000Z","dependencies_parsed_at":"2024-01-26T16:47:03.582Z","dependency_job_id":"a71e372d-2106-42ca-af12-d17435ab8773","html_url":"https://github.com/mushthak/nova-blend-salon-case-study","commit_stats":null,"previous_names":["mushthak/nova-blend-salon-case-study"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mushthak%2Fnova-blend-salon-case-study","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mushthak%2Fnova-blend-salon-case-study/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mushthak%2Fnova-blend-salon-case-study/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mushthak%2Fnova-blend-salon-case-study/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mushthak","download_url":"https://codeload.github.com/mushthak/nova-blend-salon-case-study/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243841230,"owners_count":20356446,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["ci-cd","clean-architecture","composition","modular","tdd"],"created_at":"2025-01-22T20:14:54.148Z","updated_at":"2025-03-16T08:09:05.356Z","avatar_url":"https://github.com/mushthak.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NovaBlendSalon App Case Study\n\n[![CI-macOS](https://github.com/mushthak/nova-blend-salon-case-study/actions/workflows/CI-macOS.yml/badge.svg)](https://github.com/mushthak/nova-blend-salon-case-study/actions/workflows/CI-macOS.yml)\n[![CI-iOS](https://github.com/mushthak/nova-blend-salon-case-study/actions/workflows/CI-iOS.yml/badge.svg)](https://github.com/mushthak/nova-blend-salon-case-study/actions/workflows/CI-iOS.yml)\n# Salon list Feature Specs\n\n## 1. Story: Customer requests to see available salons\n\n### Narrative #1\n\n```\nAs customer\nI want the app to show the latest remote salons\nSo I can always get the latest salon details \n```\n\n#### Scenarios (Acceptance criteria)\n\n```\nGiven the customer has connectivity\n  And customer requests to see salons\n Then the app should display the available salon list from remote\n  And replace the cache with the new salon\n\n```\n\n### Narrative #2\n\n```\nAs an offline customer\nI want the app to show the latest saved salons\nSo I can always see the salon details \n```\n\n#### Scenarios (Acceptance criteria)\n\n```\nGiven the customer doesn't have connectivity\n  And there’s a cached version of the salons\n  And the cache is less than seven days old\n When the customer requests to see the salons\n Then the app should display the latest salons saved\n ```\n\n ```\nGiven the customer doesn't have connectivity\n  And there’s a cached version of the salons\n  And the cache is older than seven days old\n When the customer requests to see the salons\n Then the app should show empty list and delete cache\n ```\n\n  ```\nGiven the customer doesn't have connectivity\n  And the cache is empty\n When the customer requests to see the salons\n Then the app should show empty list\n ```\n---\n\n## Use Cases\n\n### Load Salons Data From Remote Use Case\n\n#### Data:\n- URL\n\n#### Primary course (happy path):\n1. Execute \"Load Salons\" command with above data.\n2. System downloads data from the URL.\n3. System validates downloaded data.\n4. System delivers salon data.\n\n#### Invalid data – error course (sad path):\n1. System delivers invalid data error.\n\n#### No connectivity – error course (sad path):\n1. System delivers connectivity error.\n\n---\n\n### Load Salons From Cache Use Case\n\n#### Primary course:\n1. Execute \"Load salons\" command.\n2. System retrieves salon data from cache.\n3. System validates cache is less than seven days old.\n4. System creates salon list from cached data.\n5. System delivers salon list.\n\n#### Retrieval error course (sad path):\n1. System delivers error.\n\n#### Expired cache course (sad path): \n1. System delivers no salons.\n\n#### Empty cache course (sad path): \n1. System delivers no salons.\n\n---\n\n### Validate Salon Cache Use Case\n\n#### Primary course:\n1. Execute \"Validate Cache\" command with above data.\n2. System retrieves salon data from cache.\n3. System validates cache is less than seven days old.\n\n#### Retrieval error course (sad path):\n1. System deletes cache.\n\n#### Expired cache course (sad path): \n1. System deletes cache.\n\n---\n\n### Cache Salons Use Case\n\n#### Data:\n- Salons\n\n#### Primary course (happy path):\n1. Execute \"Save salons\" command with above data.\n2. System deletes old cache data.\n3. System encodes salons.\n4. System timestamps the new cache.\n5. System saves new cache data.\n6. System delivers success message.\n\n#### Deleting error course (sad path):\n1. System delivers error.\n\n#### Saving error course (sad path):\n1. System delivers error.\n\n---\n\n## Flowchart\n\n![NovaBlendSalon-Flowchart drawio](https://github.com/mushthak/nova-blend-salon-case-study/assets/11793859/279814bb-1bb3-49d1-afd2-f903f1e5b353)\n\n\n\n\n## Model Specs\n\n### Salon\n\n| Property      | Type                |\n|---------------|---------------------|\n| `id`          | `UUID`              |\n| `name`        | `String`            |\n| `location`    | `String`            |\n| `phone`       | `String` (optional) |\n| `openTime`    | `Float`             |\n| `closeTime`   | `Float`             |\n\n### Payload contract\n\n```\nGET *url* (TBD)\n\n200 RESPONSE\n\n{\n    \"salons\": [\n        {\n            \"id\": \"a UUID\",\n            \"name\": \"a name\",\n            \"location\": \"a location\",\n            \"phone\": \"a phone number\",\n            \"open_time\": 9.30\n            \"close_time\": 22.30,\n        },\n        {\n            \"id\": \"another UUID\",\n            \"name\": \"another name\",\n            \"location\": \"another location\",\n            \"open_time\": 10.30\n            \"close_time\": 20.30,\n        },\n        {\n            \"id\": \"even another UUID\",\n            \"name\": \"even another name\",\n            \"location\": \"even another location\",\n            \"open_time\": 8.00\n            \"close_time\": 14.00,\n        },\n        {\n            \"id\": \"yet another UUID\",\n            \"name\": \"yet another name\",\n            \"location\": \"yet another location\",\n            \"open_time\": 11.00\n            \"close_time\": 16.00,\n        }\n        ...\n    ]\n}\n```\n\n\n## 2. Story: Customer requests an appointment with a salon\n\n### Narrative #1\n\n```\nAs customer\nI want the book an appointment with a salon on my preferred date and time slot\nso that I can secure a convenient time for my service.\n```\n\n#### Scenarios (Acceptance criteria)\n\n```\nGiven the customer has connectivity\n  And customer selects a preferred date and time slot\nWhen the customer requests to book an appointment with a salon  \nThen the app should confirm the availability of the selected salon and time slot from the remote\n And book the appointment for the customer\n And update the cache with the new appointment details\n ```\n\n ```\n Given the customer does not have connectivity\n\tAnd the customer selects a preferred date and time slot\n When the customer attempts to book an appointment with a salon\n\tThen the app should display an error message indicating that booking requires an internet connection\n ```\n## Model Specs \n### SalonAppointment\n\n| Property      | Type                |\n|---------------|---------------------|\n| `id`          | `UUID`              |\n| `time`        | `Date` (ISO8601)    |\n| `phone`       | `String`            |\n| `email`       | `String` (optional) |\n| `note`        | `String` (optional) |\n\n### Payload contract\n\n```\nPOST *url* (TBD)\n\nREQUEST\n\n{\n  \"salonId\": \"a UUID\",\n  \"appointmentTime\" : \"2024-10-20T11:24:59+0000\",\n  \"phone\": \"a phone number\",\n  \"email\": \"an email id\",\n  \"notes\": \"a note\",\n}\n\n201 RESPONSE\n\n{\n  \"salonId\": \"a UUID\",\n  \"appointmentTime\" : \"2024-10-20T11:24:59+0000\",\n  \"phone\": \"a phone number\",\n  \"email\": \"an email id\",\n  \"notes\": \"a note\",\n}\n```\n---\n## 3. Story: Customer requests to see booked appointments\n\n### Narrative #1\n\n```\nAs customer\nI want the app to show the the appoinments I booked\nSo I can always view the latest appointment details \n```\n\n#### Scenarios (Acceptance criteria)\n\n```\nGiven the customer has connectivity\n  And customer requests to see his booked appointments\n Then the app should display the appointment list from remote\n  And replace the cache with the new appointments\n\n```\n\n### Narrative #2\n\n```\nAs an offline customer\nI want the app to show the saved appointment bookings\nSo I can always see the booked appintment details \n```\n\n#### Scenarios (Acceptance criteria)\n\n```\nGiven the customer doesn't have connectivity\n  And there’s a cached version of the appointments\n When the customer requests to see the booked appointments\n Then the app should display the appointments saved\n ```\n\n  ```\nGiven the customer doesn't have connectivity\n  And the cache is empty\n When the customer requests to see the booked appointments\n Then the app should show empty list\n ```\n---\n\n## Use Cases\n\n### Load Booked Appointments Data From Remote Use Case\n\n#### Data:\n- URL\n\n#### Primary course (happy path):\n1. Execute \"Load Appointments\" command with above data.\n2. System downloads data from the URL.\n3. System validates downloaded data.\n4. System delivers appointment data.\n\n#### Invalid data – error course (sad path):\n1. System delivers invalid data error.\n\n#### No connectivity – error course (sad path):\n1. System delivers connectivity error.\n\n---\n\n### Load Salons From Cache Use Case\n\n#### Primary course:\n1. Execute \"Load Appointments\" command.\n2. System retrieves appointment data from cache.\n3. System creates appointment list from cached data.\n4. System delivers appointment list.\n\n#### Retrieval error course (sad path):\n1. System delivers error.\n\n#### Empty cache course (sad path): \n1. System delivers no appointments.\n\n---\n\n### Cache Appointments Use Case\n\n#### Data:\n- Appointments\n\n#### Primary course (happy path):\n1. Execute \"Save appointments\" command with above data.\n2. System deletes old cache data.\n3. System encodes appointments.\n4. System saves new cache data.\n5. System delivers success message.\n\n#### Deleting error course (sad path):\n1. System delivers error.\n\n#### Saving error course (sad path):\n1. System delivers error.\n\n---\n### Payload contract\n\n```\nGET *url* (TBD)\n\n200 RESPONSE\n\n{\n    \"appointments\": [\n        {\n          \"salonId\": \"a UUID\",\n          \"appointmentTime\" : \"2024-10-20T11:24:59+0000\",\n          \"phone\": \"a phone number\",\n          \"email\": \"an email id\",\n          \"notes\": \"a note\"\n        }\n        {\n            \"salonId\": \"another UUID\",\n            \"appointmentTime\": \"2024-10-21T11:24:59+0000\",\n            \"phone\": \"another phone number\",\n            \"email\": \"another email id\",\n            \"notes\": \"another note\"\n        },\n        {\n            \"salonId\": \"even another UUID\",\n            \"appointmentTime\": \"2024-10-22T11:24:59+0000\",\n            \"phone\": \"even another phone number\",\n            \"email\": \"even another email id\",\n            \"notes\": \"even another note\"\n        },\n        {\n            \"salonId\": \"yet another UUID\",\n            \"appointmentTime\": \"2024-10-23T11:24:59+0000\",\n            \"phone\": \"yet another phone number\",\n            \"email\": \"yet another email id\",\n            \"notes\": \"yet another note\"\n        }\n        ...\n    ]\n}\n```\n---\n\n## App Architecture\n![image description](architecture.svg)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmushthak%2Fnova-blend-salon-case-study","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmushthak%2Fnova-blend-salon-case-study","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmushthak%2Fnova-blend-salon-case-study/lists"}