├── 01-intro ├── 2-ollama │ ├── 1-ollama_server.http │ ├── 2-commands.sh │ └── compose.yaml └── 3-integrate_model │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── databases │ └── ecommerce.sql │ ├── etc │ └── request │ │ ├── retention │ │ └── course_suggestions.http │ │ └── rrss │ │ └── users.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── next.svg │ └── vercel.svg │ ├── src │ └── app │ │ ├── api │ │ ├── langchain │ │ │ └── route.ts │ │ └── modelfusion │ │ │ └── route.ts │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── page.module.css │ │ └── page.tsx │ ├── tests │ └── contexts │ │ ├── retention │ │ ├── posts │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ └── CreateRetentionPostOnPostPublished.test.ts │ │ │ │ └── find │ │ │ │ │ └── RetentionPostFinder.test.ts │ │ │ ├── domain │ │ │ │ └── RetentionPostMother.ts │ │ │ └── infrastructure │ │ │ │ └── MockRetentionPostRepository.ts │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ ├── increment_total_posts │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ ├── recalculate_average_post_likes │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ └── update_email │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ ├── domain │ │ │ └── RetentionUserMother.ts │ │ │ └── infrastructure │ │ │ └── MockRetentionUserRepository.ts │ │ ├── rrss │ │ ├── post_likes │ │ │ ├── application │ │ │ │ └── like │ │ │ │ │ └── PostLiker.test.ts │ │ │ ├── domain │ │ │ │ ├── PostLikeIdMother.ts │ │ │ │ ├── PostLikeMother.ts │ │ │ │ └── PostLikedDomainEventMother.ts │ │ │ └── infrastructure │ │ │ │ └── MockPostLikeRepository.ts │ │ ├── posts │ │ │ ├── application │ │ │ │ ├── increment_total_likes │ │ │ │ │ └── IncrementTotalPostLikesOnPostLiked.test.ts │ │ │ │ ├── publish │ │ │ │ │ └── PostPublisher.test.ts │ │ │ │ └── update_latest_likes │ │ │ │ │ └── UpdateLatestLikesOnPostLiked.test.ts │ │ │ ├── domain │ │ │ │ ├── PostContentMother.ts │ │ │ │ ├── PostIdMother.ts │ │ │ │ ├── PostLatestLikesMother.ts │ │ │ │ ├── PostLikesIncrementedDomainEventMother.ts │ │ │ │ ├── PostLikesMother.ts │ │ │ │ ├── PostMother.ts │ │ │ │ └── PostPublishedDomainEventMother.ts │ │ │ └── infrastructure │ │ │ │ └── MockPostRepository.ts │ │ └── users │ │ │ ├── application │ │ │ ├── registrar │ │ │ │ └── UserRegistrar.test.ts │ │ │ ├── search │ │ │ │ └── UserSearcher.test.ts │ │ │ └── search_by_criteria │ │ │ │ └── UsersByCriteriaSearcher.test.ts │ │ │ ├── domain │ │ │ ├── DateMother.ts │ │ │ ├── UserEmailMother.ts │ │ │ ├── UserEmailUpdatedDomainEventMother.ts │ │ │ ├── UserIdMother.ts │ │ │ ├── UserMother.ts │ │ │ ├── UserNameMother.ts │ │ │ ├── UserProfilePictureMother.ts │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ └── infrastructure │ │ │ ├── MockUserRepository.ts │ │ │ ├── MySqlUserRepository.test.ts │ │ │ └── RedisCacheUserRepository.test.ts │ │ └── shared │ │ ├── domain │ │ ├── EmailAddressMother.ts │ │ ├── EnumMother.ts │ │ └── criteria │ │ │ ├── CriteriaMother.ts │ │ │ ├── FilterMother.ts │ │ │ ├── FiltersMother.ts │ │ │ └── OrderMother.ts │ │ └── infrastructure │ │ ├── MockCache.ts │ │ ├── MockClock.ts │ │ ├── MockEventBus.ts │ │ └── criteria │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json ├── 02-software_architecture └── 2-implementation_details │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ └── codely.sql │ ├── etc │ └── request │ │ └── mooc │ │ └── users.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── next.svg │ └── vercel.svg │ ├── src │ ├── app │ │ ├── api │ │ │ └── mooc │ │ │ │ ├── user_course_suggestions │ │ │ │ └── [user_id] │ │ │ │ │ └── route.ts │ │ │ │ └── users │ │ │ │ └── [user_id] │ │ │ │ └── route.ts │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ └── page.module.css │ └── contexts │ │ ├── mooc │ │ ├── user_course_suggestions │ │ │ ├── application │ │ │ │ └── search │ │ │ │ │ └── UserCourseSuggestionsSearcher.ts │ │ │ ├── domain │ │ │ │ ├── UserCourseSuggestions.ts │ │ │ │ └── UserCourseSuggestionsRepository.ts │ │ │ └── infrastructure │ │ │ │ └── OllamaMistralUserCourseSuggestionsRepository.ts │ │ └── users │ │ │ ├── application │ │ │ ├── find │ │ │ │ └── UserFinder.ts │ │ │ └── registrar │ │ │ │ └── UserRegistrar.ts │ │ │ ├── domain │ │ │ ├── User.ts │ │ │ ├── UserDoesNotExist.ts │ │ │ ├── UserDomainEvent.ts │ │ │ ├── UserEmail.ts │ │ │ ├── UserFinder.ts │ │ │ ├── UserId.ts │ │ │ ├── UserName.ts │ │ │ ├── UserProfilePicture.ts │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ ├── UserRepository.ts │ │ │ └── UserStatus.ts │ │ │ └── infrastructure │ │ │ └── MySqlUserRepository.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ └── RetentionUserCreator.ts │ │ │ ├── increment_total_posts │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ ├── recalculate_average_post_likes │ │ │ │ ├── RecalculateRetentionUserAveragePostLikesOnPostLiked.ts │ │ │ │ └── RetentionUserAveragePostLikesRecalculator.ts │ │ │ └── update_email │ │ │ │ ├── RetentionUserEmailUpdater.ts │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.ts │ │ │ └── domain │ │ │ ├── RetentionUser.ts │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ └── RetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── AggregateRoot.ts │ │ ├── Clock.ts │ │ ├── EmailAddress.ts │ │ ├── Identifier.ts │ │ ├── NumberValueObject.ts │ │ ├── StringValueObject.ts │ │ ├── criteria │ │ │ ├── Criteria.ts │ │ │ ├── Filter.ts │ │ │ ├── FilterField.ts │ │ │ ├── FilterOperator.ts │ │ │ ├── FilterValue.ts │ │ │ ├── Filters.ts │ │ │ ├── Order.ts │ │ │ ├── OrderBy.ts │ │ │ └── OrderType.ts │ │ └── event │ │ │ ├── DomainEvent.ts │ │ │ ├── DomainEventClass.ts │ │ │ ├── DomainEventSubscriber.ts │ │ │ └── EventBus.ts │ │ └── infrastructure │ │ ├── MariaDBConnection.ts │ │ ├── RedisClient.ts │ │ └── criteria │ │ ├── CriteriaToSqlConverter.ts │ │ └── SearchParamsCriteriaFiltersParser.ts │ ├── tests │ └── contexts │ │ ├── mooc │ │ └── users │ │ │ ├── application │ │ │ ├── registrar │ │ │ │ └── UserRegistrar.test.ts │ │ │ └── search │ │ │ │ └── UserSearcher.test.ts │ │ │ ├── domain │ │ │ ├── DateMother.ts │ │ │ ├── UserEmailMother.ts │ │ │ ├── UserIdMother.ts │ │ │ ├── UserMother.ts │ │ │ ├── UserNameMother.ts │ │ │ ├── UserProfilePictureMother.ts │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ └── infrastructure │ │ │ ├── MockUserRepository.ts │ │ │ └── MySqlUserRepository.test.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ ├── increment_total_posts │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ ├── recalculate_average_post_likes │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ └── update_email │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ ├── domain │ │ │ └── RetentionUserMother.ts │ │ │ └── infrastructure │ │ │ └── MockRetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── EmailAddressMother.ts │ │ ├── EnumMother.ts │ │ └── criteria │ │ │ ├── CriteriaMother.ts │ │ │ ├── FilterMother.ts │ │ │ ├── FiltersMother.ts │ │ │ └── OrderMother.ts │ │ └── infrastructure │ │ ├── MockCache.ts │ │ ├── MockClock.ts │ │ ├── MockEventBus.ts │ │ └── criteria │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json ├── 03-user_profile_wth_suggestions ├── 1-llm_use_case │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ │ └── codely.sql │ ├── etc │ │ └── request │ │ │ ├── mooc │ │ │ └── users.http │ │ │ └── retention │ │ │ └── course_suggestions.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── src │ │ ├── app │ │ │ ├── api │ │ │ │ └── mooc │ │ │ │ │ └── users │ │ │ │ │ └── [user_id] │ │ │ │ │ └── route.ts │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ ├── layout.tsx │ │ │ ├── page.module.css │ │ │ └── page.tsx │ │ └── contexts │ │ │ ├── mooc │ │ │ ├── course_suggestions │ │ │ │ ├── domain │ │ │ │ │ └── CourseSuggestionsRepository.ts │ │ │ │ └── infrastructure │ │ │ │ │ └── OllamaMistralCoursesSuggestionsRepository.ts │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── find │ │ │ │ │ └── UserFinder.ts │ │ │ │ └── registrar │ │ │ │ │ └── UserRegistrar.ts │ │ │ │ ├── domain │ │ │ │ ├── User.ts │ │ │ │ ├── UserDoesNotExist.ts │ │ │ │ ├── UserDomainEvent.ts │ │ │ │ ├── UserEmail.ts │ │ │ │ ├── UserFinder.ts │ │ │ │ ├── UserId.ts │ │ │ │ ├── UserName.ts │ │ │ │ ├── UserProfilePicture.ts │ │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ │ ├── UserRepository.ts │ │ │ │ └── UserStatus.ts │ │ │ │ └── infrastructure │ │ │ │ └── MySqlUserRepository.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ │ └── RetentionUserCreator.ts │ │ │ │ ├── increment_total_posts │ │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ │ ├── recalculate_average_post_likes │ │ │ │ │ ├── RecalculateRetentionUserAveragePostLikesOnPostLiked.ts │ │ │ │ │ └── RetentionUserAveragePostLikesRecalculator.ts │ │ │ │ └── update_email │ │ │ │ │ ├── RetentionUserEmailUpdater.ts │ │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.ts │ │ │ │ └── domain │ │ │ │ ├── RetentionUser.ts │ │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ │ └── RetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── AggregateRoot.ts │ │ │ ├── Clock.ts │ │ │ ├── EmailAddress.ts │ │ │ ├── Identifier.ts │ │ │ ├── NumberValueObject.ts │ │ │ ├── StringValueObject.ts │ │ │ ├── criteria │ │ │ │ ├── Criteria.ts │ │ │ │ ├── Filter.ts │ │ │ │ ├── FilterField.ts │ │ │ │ ├── FilterOperator.ts │ │ │ │ ├── FilterValue.ts │ │ │ │ ├── Filters.ts │ │ │ │ ├── Order.ts │ │ │ │ ├── OrderBy.ts │ │ │ │ └── OrderType.ts │ │ │ └── event │ │ │ │ ├── DomainEvent.ts │ │ │ │ ├── DomainEventClass.ts │ │ │ │ ├── DomainEventSubscriber.ts │ │ │ │ └── EventBus.ts │ │ │ └── infrastructure │ │ │ ├── MariaDBConnection.ts │ │ │ ├── RedisClient.ts │ │ │ └── criteria │ │ │ ├── CriteriaToSqlConverter.ts │ │ │ └── SearchParamsCriteriaFiltersParser.ts │ ├── tests │ │ └── contexts │ │ │ ├── mooc │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── registrar │ │ │ │ │ └── UserRegistrar.test.ts │ │ │ │ └── search │ │ │ │ │ └── UserSearcher.test.ts │ │ │ │ ├── domain │ │ │ │ ├── DateMother.ts │ │ │ │ ├── UserEmailMother.ts │ │ │ │ ├── UserIdMother.ts │ │ │ │ ├── UserMother.ts │ │ │ │ ├── UserNameMother.ts │ │ │ │ ├── UserProfilePictureMother.ts │ │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ │ └── infrastructure │ │ │ │ ├── MockUserRepository.ts │ │ │ │ └── MySqlUserRepository.test.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ │ ├── increment_total_posts │ │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ │ ├── recalculate_average_post_likes │ │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ │ └── update_email │ │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ │ ├── domain │ │ │ │ └── RetentionUserMother.ts │ │ │ │ └── infrastructure │ │ │ │ └── MockRetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── EmailAddressMother.ts │ │ │ ├── EnumMother.ts │ │ │ └── criteria │ │ │ │ ├── CriteriaMother.ts │ │ │ │ ├── FilterMother.ts │ │ │ │ ├── FiltersMother.ts │ │ │ │ └── OrderMother.ts │ │ │ └── infrastructure │ │ │ ├── MockCache.ts │ │ │ ├── MockClock.ts │ │ │ ├── MockEventBus.ts │ │ │ └── criteria │ │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json └── 2-llm_in_repo │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ └── codely.sql │ ├── etc │ └── request │ │ ├── mooc │ │ └── users.http │ │ └── retention │ │ └── course_suggestions.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── next.svg │ └── vercel.svg │ ├── src │ ├── app │ │ ├── api │ │ │ └── mooc │ │ │ │ └── users │ │ │ │ └── [user_id] │ │ │ │ └── route.ts │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── page.module.css │ │ └── page.tsx │ └── contexts │ │ ├── mooc │ │ ├── course_suggestions │ │ │ └── domain │ │ │ │ └── CourseSuggestionsRepository.ts │ │ └── users │ │ │ ├── application │ │ │ ├── find │ │ │ │ └── UserFinder.ts │ │ │ └── registrar │ │ │ │ └── UserRegistrar.ts │ │ │ ├── domain │ │ │ ├── User.ts │ │ │ ├── UserDoesNotExist.ts │ │ │ ├── UserDomainEvent.ts │ │ │ ├── UserEmail.ts │ │ │ ├── UserFinder.ts │ │ │ ├── UserId.ts │ │ │ ├── UserName.ts │ │ │ ├── UserProfilePicture.ts │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ ├── UserRepository.ts │ │ │ └── UserStatus.ts │ │ │ └── infrastructure │ │ │ ├── MySqlUserRepository.ts │ │ │ └── OllamaMistralCourseSuggestionsRepository.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ └── RetentionUserCreator.ts │ │ │ ├── increment_total_posts │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ ├── recalculate_average_post_likes │ │ │ │ ├── RecalculateRetentionUserAveragePostLikesOnPostLiked.ts │ │ │ │ └── RetentionUserAveragePostLikesRecalculator.ts │ │ │ └── update_email │ │ │ │ ├── RetentionUserEmailUpdater.ts │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.ts │ │ │ └── domain │ │ │ ├── RetentionUser.ts │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ └── RetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── AggregateRoot.ts │ │ ├── Clock.ts │ │ ├── EmailAddress.ts │ │ ├── Identifier.ts │ │ ├── NumberValueObject.ts │ │ ├── StringValueObject.ts │ │ ├── criteria │ │ │ ├── Criteria.ts │ │ │ ├── Filter.ts │ │ │ ├── FilterField.ts │ │ │ ├── FilterOperator.ts │ │ │ ├── FilterValue.ts │ │ │ ├── Filters.ts │ │ │ ├── Order.ts │ │ │ ├── OrderBy.ts │ │ │ └── OrderType.ts │ │ └── event │ │ │ ├── DomainEvent.ts │ │ │ ├── DomainEventClass.ts │ │ │ ├── DomainEventSubscriber.ts │ │ │ └── EventBus.ts │ │ └── infrastructure │ │ ├── MariaDBConnection.ts │ │ ├── RedisClient.ts │ │ └── criteria │ │ ├── CriteriaToSqlConverter.ts │ │ └── SearchParamsCriteriaFiltersParser.ts │ ├── tests │ └── contexts │ │ ├── mooc │ │ └── users │ │ │ ├── application │ │ │ ├── registrar │ │ │ │ └── UserRegistrar.test.ts │ │ │ └── search │ │ │ │ └── UserSearcher.test.ts │ │ │ ├── domain │ │ │ ├── DateMother.ts │ │ │ ├── UserEmailMother.ts │ │ │ ├── UserIdMother.ts │ │ │ ├── UserMother.ts │ │ │ ├── UserNameMother.ts │ │ │ ├── UserProfilePictureMother.ts │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ └── infrastructure │ │ │ ├── MockUserRepository.ts │ │ │ └── MySqlUserRepository.test.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ ├── increment_total_posts │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ ├── recalculate_average_post_likes │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ └── update_email │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ ├── domain │ │ │ └── RetentionUserMother.ts │ │ │ └── infrastructure │ │ │ └── MockRetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── EmailAddressMother.ts │ │ ├── EnumMother.ts │ │ └── criteria │ │ │ ├── CriteriaMother.ts │ │ │ ├── FilterMother.ts │ │ │ ├── FiltersMother.ts │ │ │ └── OrderMother.ts │ │ └── infrastructure │ │ ├── MockCache.ts │ │ ├── MockClock.ts │ │ ├── MockEventBus.ts │ │ └── criteria │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json ├── 04-llm_implementation_details ├── 1-implement_a_recommendator │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ │ └── codely.sql │ ├── etc │ │ └── request │ │ │ └── mooc │ │ │ └── users.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── src │ │ ├── app │ │ │ ├── api │ │ │ │ └── mooc │ │ │ │ │ └── users │ │ │ │ │ └── [user_id] │ │ │ │ │ └── route.ts │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ ├── layout.tsx │ │ │ └── page.module.css │ │ └── contexts │ │ │ ├── mooc │ │ │ ├── user_course_suggestions │ │ │ │ ├── application │ │ │ │ │ └── search │ │ │ │ │ │ └── UserCourseSuggestionsSearcher.ts │ │ │ │ ├── domain │ │ │ │ │ ├── UserCourseSuggestions.ts │ │ │ │ │ └── UserCourseSuggestionsRepository.ts │ │ │ │ └── infrastructure │ │ │ │ │ └── OllamaMistralUserCourseSuggestionsRepository.ts │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── find │ │ │ │ │ └── UserFinder.ts │ │ │ │ └── registrar │ │ │ │ │ └── UserRegistrar.ts │ │ │ │ ├── domain │ │ │ │ ├── User.ts │ │ │ │ ├── UserDoesNotExist.ts │ │ │ │ ├── UserDomainEvent.ts │ │ │ │ ├── UserEmail.ts │ │ │ │ ├── UserFinder.ts │ │ │ │ ├── UserId.ts │ │ │ │ ├── UserName.ts │ │ │ │ ├── UserProfilePicture.ts │ │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ │ ├── UserRepository.ts │ │ │ │ └── UserStatus.ts │ │ │ │ └── infrastructure │ │ │ │ └── MySqlUserRepository.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ │ └── RetentionUserCreator.ts │ │ │ │ ├── increment_total_posts │ │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ │ ├── recalculate_average_post_likes │ │ │ │ │ ├── RecalculateRetentionUserAveragePostLikesOnPostLiked.ts │ │ │ │ │ └── RetentionUserAveragePostLikesRecalculator.ts │ │ │ │ └── update_email │ │ │ │ │ ├── RetentionUserEmailUpdater.ts │ │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.ts │ │ │ │ └── domain │ │ │ │ ├── RetentionUser.ts │ │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ │ └── RetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── AggregateRoot.ts │ │ │ ├── Clock.ts │ │ │ ├── EmailAddress.ts │ │ │ ├── Identifier.ts │ │ │ ├── NumberValueObject.ts │ │ │ ├── StringValueObject.ts │ │ │ ├── criteria │ │ │ │ ├── Criteria.ts │ │ │ │ ├── Filter.ts │ │ │ │ ├── FilterField.ts │ │ │ │ ├── FilterOperator.ts │ │ │ │ ├── FilterValue.ts │ │ │ │ ├── Filters.ts │ │ │ │ ├── Order.ts │ │ │ │ ├── OrderBy.ts │ │ │ │ └── OrderType.ts │ │ │ └── event │ │ │ │ ├── DomainEvent.ts │ │ │ │ ├── DomainEventClass.ts │ │ │ │ ├── DomainEventSubscriber.ts │ │ │ │ └── EventBus.ts │ │ │ └── infrastructure │ │ │ ├── MariaDBConnection.ts │ │ │ ├── RedisClient.ts │ │ │ └── criteria │ │ │ ├── CriteriaToSqlConverter.ts │ │ │ └── SearchParamsCriteriaFiltersParser.ts │ ├── tests │ │ └── contexts │ │ │ ├── mooc │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── registrar │ │ │ │ │ └── UserRegistrar.test.ts │ │ │ │ └── search │ │ │ │ │ └── UserSearcher.test.ts │ │ │ │ ├── domain │ │ │ │ ├── DateMother.ts │ │ │ │ ├── UserEmailMother.ts │ │ │ │ ├── UserIdMother.ts │ │ │ │ ├── UserMother.ts │ │ │ │ ├── UserNameMother.ts │ │ │ │ ├── UserProfilePictureMother.ts │ │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ │ └── infrastructure │ │ │ │ ├── MockUserRepository.ts │ │ │ │ └── MySqlUserRepository.test.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ │ ├── increment_total_posts │ │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ │ ├── recalculate_average_post_likes │ │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ │ └── update_email │ │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ │ ├── domain │ │ │ │ └── RetentionUserMother.ts │ │ │ │ └── infrastructure │ │ │ │ └── MockRetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── EmailAddressMother.ts │ │ │ ├── EnumMother.ts │ │ │ └── criteria │ │ │ │ ├── CriteriaMother.ts │ │ │ │ ├── FilterMother.ts │ │ │ │ ├── FiltersMother.ts │ │ │ │ └── OrderMother.ts │ │ │ └── infrastructure │ │ │ ├── MockCache.ts │ │ │ ├── MockClock.ts │ │ │ ├── MockEventBus.ts │ │ │ └── criteria │ │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json └── 2-cache │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ └── codely.sql │ ├── etc │ └── request │ │ └── mooc │ │ └── users.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── next.svg │ └── vercel.svg │ ├── src │ ├── app │ │ ├── api │ │ │ └── mooc │ │ │ │ └── users │ │ │ │ └── [user_id] │ │ │ │ └── route.ts │ │ ├── favicon.ico │ │ ├── globals.css │ │ └── layout.tsx │ └── contexts │ │ ├── mooc │ │ ├── user_course_suggestions │ │ │ ├── application │ │ │ │ └── search │ │ │ │ │ └── UserCourseSuggestionsSearcher.ts │ │ │ ├── domain │ │ │ │ ├── UserCourseSuggestions.ts │ │ │ │ └── UserCourseSuggestionsRepository.ts │ │ │ └── infrastructure │ │ │ │ └── OllamaMistralUserCourseSuggestionsRepository.ts │ │ └── users │ │ │ ├── application │ │ │ ├── find │ │ │ │ └── UserFinder.ts │ │ │ └── registrar │ │ │ │ └── UserRegistrar.ts │ │ │ ├── domain │ │ │ ├── User.ts │ │ │ ├── UserDoesNotExist.ts │ │ │ ├── UserDomainEvent.ts │ │ │ ├── UserEmail.ts │ │ │ ├── UserFinder.ts │ │ │ ├── UserId.ts │ │ │ ├── UserName.ts │ │ │ ├── UserProfilePicture.ts │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ ├── UserRepository.ts │ │ │ └── UserStatus.ts │ │ │ └── infrastructure │ │ │ ├── InMemoryCacheUserRepository.ts │ │ │ └── MySqlUserRepository.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ └── RetentionUserCreator.ts │ │ │ ├── increment_total_posts │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ ├── recalculate_average_post_likes │ │ │ │ ├── RecalculateRetentionUserAveragePostLikesOnPostLiked.ts │ │ │ │ └── RetentionUserAveragePostLikesRecalculator.ts │ │ │ └── update_email │ │ │ │ ├── RetentionUserEmailUpdater.ts │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.ts │ │ │ └── domain │ │ │ ├── RetentionUser.ts │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ └── RetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── AggregateRoot.ts │ │ ├── Clock.ts │ │ ├── EmailAddress.ts │ │ ├── Identifier.ts │ │ ├── NumberValueObject.ts │ │ ├── StringValueObject.ts │ │ ├── criteria │ │ │ ├── Criteria.ts │ │ │ ├── Filter.ts │ │ │ ├── FilterField.ts │ │ │ ├── FilterOperator.ts │ │ │ ├── FilterValue.ts │ │ │ ├── Filters.ts │ │ │ ├── Order.ts │ │ │ ├── OrderBy.ts │ │ │ └── OrderType.ts │ │ └── event │ │ │ ├── DomainEvent.ts │ │ │ ├── DomainEventClass.ts │ │ │ ├── DomainEventSubscriber.ts │ │ │ └── EventBus.ts │ │ └── infrastructure │ │ ├── MariaDBConnection.ts │ │ ├── RedisClient.ts │ │ └── criteria │ │ ├── CriteriaToSqlConverter.ts │ │ └── SearchParamsCriteriaFiltersParser.ts │ ├── tests │ └── contexts │ │ ├── mooc │ │ └── users │ │ │ ├── application │ │ │ ├── registrar │ │ │ │ └── UserRegistrar.test.ts │ │ │ └── search │ │ │ │ └── UserSearcher.test.ts │ │ │ ├── domain │ │ │ ├── DateMother.ts │ │ │ ├── UserEmailMother.ts │ │ │ ├── UserIdMother.ts │ │ │ ├── UserMother.ts │ │ │ ├── UserNameMother.ts │ │ │ ├── UserProfilePictureMother.ts │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ └── infrastructure │ │ │ ├── MockUserRepository.ts │ │ │ └── MySqlUserRepository.test.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ ├── increment_total_posts │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ ├── recalculate_average_post_likes │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ └── update_email │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ ├── domain │ │ │ └── RetentionUserMother.ts │ │ │ └── infrastructure │ │ │ └── MockRetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── EmailAddressMother.ts │ │ ├── EnumMother.ts │ │ └── criteria │ │ │ ├── CriteriaMother.ts │ │ │ ├── FilterMother.ts │ │ │ ├── FiltersMother.ts │ │ │ └── OrderMother.ts │ │ └── infrastructure │ │ ├── MockCache.ts │ │ ├── MockClock.ts │ │ ├── MockEventBus.ts │ │ └── criteria │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json ├── 05-event_driven └── 2-llm_with_domain_events │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ └── codely.sql │ ├── etc │ └── request │ │ ├── mooc │ │ ├── course_progress.http │ │ └── users.http │ │ └── retention │ │ └── course_suggestions.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── next.svg │ └── vercel.svg │ ├── src │ ├── app │ │ ├── api │ │ │ └── mooc │ │ │ │ ├── course_progress │ │ │ │ └── route.ts │ │ │ │ └── users │ │ │ │ └── [user_id] │ │ │ │ └── route.ts │ │ ├── favicon.ico │ │ ├── globals.css │ │ └── layout.tsx │ └── contexts │ │ ├── mooc │ │ ├── user_course_progress │ │ │ ├── application │ │ │ │ └── complete │ │ │ │ │ └── UserCourseProgressCompleter.ts │ │ │ └── domain │ │ │ │ └── UserCourseProgressCompletedDomainEvent.ts │ │ ├── user_course_suggestions │ │ │ ├── application │ │ │ │ ├── generate │ │ │ │ │ ├── GenerateUserCourseSuggestionsOnUserCourseProgressCompleted.ts │ │ │ │ │ └── UserCourseSuggestionsGenerator.ts │ │ │ │ └── search │ │ │ │ │ └── UserCourseSuggestionsSearcher.ts │ │ │ ├── domain │ │ │ │ ├── CourseSuggestionsGenerator.ts │ │ │ │ ├── UserCourseSuggestions.ts │ │ │ │ ├── UserCourseSuggestionsGenerated.ts │ │ │ │ └── UserCourseSuggestionsRepository.ts │ │ │ └── infrastructure │ │ │ │ ├── MySqlUserCourseSuggestionsRepository.ts │ │ │ │ └── OllamaMistralCourseSuggestionsGenerator.ts │ │ └── users │ │ │ ├── application │ │ │ ├── find │ │ │ │ └── UserFinder.ts │ │ │ ├── registrar │ │ │ │ └── UserRegistrar.ts │ │ │ └── update_course_suggestions │ │ │ │ ├── UpdateUserCourseSuggestionsOnUserCourseSuggestionsGenerated.ts │ │ │ │ └── UserCourseSuggestionsUpdater.ts │ │ │ ├── domain │ │ │ ├── CoursesSuggestionLlm.ts │ │ │ ├── User.ts │ │ │ ├── UserDoesNotExist.ts │ │ │ ├── UserDomainEvent.ts │ │ │ ├── UserEmail.ts │ │ │ ├── UserFinder.ts │ │ │ ├── UserId.ts │ │ │ ├── UserName.ts │ │ │ ├── UserProfilePicture.ts │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ ├── UserRepository.ts │ │ │ └── UserStatus.ts │ │ │ └── infrastructure │ │ │ ├── InMemoryCacheUserRepository.ts │ │ │ └── MySqlUserRepository.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ └── RetentionUserCreator.ts │ │ │ └── increment_total_posts │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ └── domain │ │ │ ├── RetentionUser.ts │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ └── RetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── AggregateRoot.ts │ │ ├── Clock.ts │ │ ├── EmailAddress.ts │ │ ├── Identifier.ts │ │ ├── NumberValueObject.ts │ │ ├── StringValueObject.ts │ │ ├── criteria │ │ │ ├── Criteria.ts │ │ │ ├── Filter.ts │ │ │ ├── FilterField.ts │ │ │ ├── FilterOperator.ts │ │ │ ├── FilterValue.ts │ │ │ ├── Filters.ts │ │ │ ├── Order.ts │ │ │ ├── OrderBy.ts │ │ │ └── OrderType.ts │ │ └── event │ │ │ ├── DomainEvent.ts │ │ │ ├── DomainEventClass.ts │ │ │ ├── DomainEventSubscriber.ts │ │ │ └── EventBus.ts │ │ └── infrastructure │ │ ├── MariaDBConnection.ts │ │ ├── RedisClient.ts │ │ ├── criteria │ │ ├── CriteriaToSqlConverter.ts │ │ └── SearchParamsCriteriaFiltersParser.ts │ │ └── domain_event │ │ └── InMemoryEventBus.ts │ ├── tests │ └── contexts │ │ ├── mooc │ │ ├── courses_progress │ │ │ └── domain │ │ │ │ └── CourseProgressFinishedDomainEventMother.ts │ │ └── users │ │ │ ├── application │ │ │ ├── registrar │ │ │ │ └── UserRegistrar.test.ts │ │ │ └── search │ │ │ │ └── UserSearcher.test.ts │ │ │ ├── domain │ │ │ ├── DateMother.ts │ │ │ ├── UserEmailMother.ts │ │ │ ├── UserIdMother.ts │ │ │ ├── UserMother.ts │ │ │ ├── UserNameMother.ts │ │ │ ├── UserProfilePictureMother.ts │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ └── infrastructure │ │ │ ├── MockCoursesSuggestionLlm.ts │ │ │ ├── MockUserRepository.ts │ │ │ └── MySqlUserRepository.test.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ ├── increment_total_posts │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ ├── recalculate_average_post_likes │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ └── update_email │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ ├── domain │ │ │ └── RetentionUserMother.ts │ │ │ └── infrastructure │ │ │ └── MockRetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── EmailAddressMother.ts │ │ ├── EnumMother.ts │ │ └── criteria │ │ │ ├── CriteriaMother.ts │ │ │ ├── FilterMother.ts │ │ │ ├── FiltersMother.ts │ │ │ └── OrderMother.ts │ │ └── infrastructure │ │ ├── MockCache.ts │ │ ├── MockClock.ts │ │ ├── MockEventBus.ts │ │ └── criteria │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json ├── 06-chatgpt ├── 1-add_chatgpt │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ │ └── codely.sql │ ├── etc │ │ └── request │ │ │ └── mooc │ │ │ ├── course_progress.http │ │ │ └── users.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── src │ │ ├── app │ │ │ ├── api │ │ │ │ └── mooc │ │ │ │ │ ├── course_progress │ │ │ │ │ └── route.ts │ │ │ │ │ └── users │ │ │ │ │ └── [user_id] │ │ │ │ │ └── route.ts │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ └── layout.tsx │ │ └── contexts │ │ │ ├── mooc │ │ │ ├── user_course_progress │ │ │ │ ├── application │ │ │ │ │ └── complete │ │ │ │ │ │ └── UserCourseProgressCompleter.ts │ │ │ │ └── domain │ │ │ │ │ └── UserCourseProgressCompletedDomainEvent.ts │ │ │ ├── user_course_suggestions │ │ │ │ ├── application │ │ │ │ │ ├── generate │ │ │ │ │ │ ├── GenerateUserCourseSuggestionsOnUserCourseProgressCompleted.ts │ │ │ │ │ │ └── UserCourseSuggestionsGenerator.ts │ │ │ │ │ └── search │ │ │ │ │ │ └── UserCourseSuggestionsSearcher.ts │ │ │ │ ├── domain │ │ │ │ │ ├── CourseSuggestionsGenerator.ts │ │ │ │ │ ├── UserCourseSuggestions.ts │ │ │ │ │ ├── UserCourseSuggestionsGenerated.ts │ │ │ │ │ └── UserCourseSuggestionsRepository.ts │ │ │ │ └── infrastructure │ │ │ │ │ ├── MySqlUserCourseSuggestionsRepository.ts │ │ │ │ │ ├── OllamaMistralCourseSuggestionsGenerator.ts │ │ │ │ │ └── OpenAIChatGPT35CourseSuggestionsGenerator.ts │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── find │ │ │ │ │ └── UserFinder.ts │ │ │ │ ├── registrar │ │ │ │ │ └── UserRegistrar.ts │ │ │ │ └── update_course_suggestions │ │ │ │ │ ├── UpdateUserCourseSuggestionsOnUserCourseSuggestionsGenerated.ts │ │ │ │ │ └── UserCourseSuggestionsUpdater.ts │ │ │ │ ├── domain │ │ │ │ ├── CoursesSuggestionLlm.ts │ │ │ │ ├── User.ts │ │ │ │ ├── UserDoesNotExist.ts │ │ │ │ ├── UserDomainEvent.ts │ │ │ │ ├── UserEmail.ts │ │ │ │ ├── UserFinder.ts │ │ │ │ ├── UserId.ts │ │ │ │ ├── UserName.ts │ │ │ │ ├── UserProfilePicture.ts │ │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ │ ├── UserRepository.ts │ │ │ │ └── UserStatus.ts │ │ │ │ └── infrastructure │ │ │ │ ├── InMemoryCacheUserRepository.ts │ │ │ │ └── MySqlUserRepository.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ │ └── RetentionUserCreator.ts │ │ │ │ └── increment_total_posts │ │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ │ └── domain │ │ │ │ ├── RetentionUser.ts │ │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ │ └── RetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── AggregateRoot.ts │ │ │ ├── Clock.ts │ │ │ ├── EmailAddress.ts │ │ │ ├── Identifier.ts │ │ │ ├── NumberValueObject.ts │ │ │ ├── StringValueObject.ts │ │ │ ├── criteria │ │ │ │ ├── Criteria.ts │ │ │ │ ├── Filter.ts │ │ │ │ ├── FilterField.ts │ │ │ │ ├── FilterOperator.ts │ │ │ │ ├── FilterValue.ts │ │ │ │ ├── Filters.ts │ │ │ │ ├── Order.ts │ │ │ │ ├── OrderBy.ts │ │ │ │ └── OrderType.ts │ │ │ └── event │ │ │ │ ├── DomainEvent.ts │ │ │ │ ├── DomainEventClass.ts │ │ │ │ ├── DomainEventSubscriber.ts │ │ │ │ └── EventBus.ts │ │ │ └── infrastructure │ │ │ ├── MariaDBConnection.ts │ │ │ ├── RedisClient.ts │ │ │ ├── criteria │ │ │ ├── CriteriaToSqlConverter.ts │ │ │ └── SearchParamsCriteriaFiltersParser.ts │ │ │ └── domain_event │ │ │ └── InMemoryEventBus.ts │ ├── tests │ │ └── contexts │ │ │ ├── mooc │ │ │ ├── courses_progress │ │ │ │ └── domain │ │ │ │ │ └── CourseProgressFinishedDomainEventMother.ts │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── registrar │ │ │ │ │ └── UserRegistrar.test.ts │ │ │ │ └── search │ │ │ │ │ └── UserSearcher.test.ts │ │ │ │ ├── domain │ │ │ │ ├── DateMother.ts │ │ │ │ ├── UserEmailMother.ts │ │ │ │ ├── UserIdMother.ts │ │ │ │ ├── UserMother.ts │ │ │ │ ├── UserNameMother.ts │ │ │ │ ├── UserProfilePictureMother.ts │ │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ │ └── infrastructure │ │ │ │ ├── MockCoursesSuggestionLlm.ts │ │ │ │ ├── MockUserRepository.ts │ │ │ │ └── MySqlUserRepository.test.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ │ ├── increment_total_posts │ │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ │ ├── recalculate_average_post_likes │ │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ │ └── update_email │ │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ │ ├── domain │ │ │ │ └── RetentionUserMother.ts │ │ │ │ └── infrastructure │ │ │ │ └── MockRetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── EmailAddressMother.ts │ │ │ ├── EnumMother.ts │ │ │ └── criteria │ │ │ │ ├── CriteriaMother.ts │ │ │ │ ├── FilterMother.ts │ │ │ │ ├── FiltersMother.ts │ │ │ │ └── OrderMother.ts │ │ │ └── infrastructure │ │ │ ├── MockCache.ts │ │ │ ├── MockClock.ts │ │ │ ├── MockEventBus.ts │ │ │ └── criteria │ │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json └── 2-tokens_calculation │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ └── codely.sql │ ├── etc │ └── request │ │ └── mooc │ │ ├── course_progress.http │ │ └── users.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── next.svg │ └── vercel.svg │ ├── src │ ├── app │ │ ├── api │ │ │ └── mooc │ │ │ │ ├── course_progress │ │ │ │ └── route.ts │ │ │ │ └── users │ │ │ │ └── [user_id] │ │ │ │ └── route.ts │ │ ├── favicon.ico │ │ ├── globals.css │ │ └── layout.tsx │ └── contexts │ │ ├── mooc │ │ ├── user_course_progress │ │ │ ├── application │ │ │ │ └── complete │ │ │ │ │ └── UserCourseProgressCompleter.ts │ │ │ └── domain │ │ │ │ └── UserCourseProgressCompletedDomainEvent.ts │ │ ├── user_course_suggestions │ │ │ ├── application │ │ │ │ ├── generate │ │ │ │ │ ├── GenerateUserCourseSuggestionsOnUserCourseProgressCompleted.ts │ │ │ │ │ └── UserCourseSuggestionsGenerator.ts │ │ │ │ └── search │ │ │ │ │ └── UserCourseSuggestionsSearcher.ts │ │ │ ├── domain │ │ │ │ ├── CourseSuggestionsGenerator.ts │ │ │ │ ├── UserCourseSuggestions.ts │ │ │ │ ├── UserCourseSuggestionsGenerated.ts │ │ │ │ └── UserCourseSuggestionsRepository.ts │ │ │ └── infrastructure │ │ │ │ ├── MySqlUserCourseSuggestionsRepository.ts │ │ │ │ ├── OllamaMistralCourseSuggestionsGenerator.ts │ │ │ │ └── OpenAIChatGPT35CourseSuggestionsGenerator.ts │ │ └── users │ │ │ ├── application │ │ │ ├── find │ │ │ │ └── UserFinder.ts │ │ │ ├── registrar │ │ │ │ └── UserRegistrar.ts │ │ │ └── update_course_suggestions │ │ │ │ ├── UpdateUserCourseSuggestionsOnUserCourseSuggestionsGenerated.ts │ │ │ │ └── UserCourseSuggestionsUpdater.ts │ │ │ ├── domain │ │ │ ├── CoursesSuggestionLlm.ts │ │ │ ├── User.ts │ │ │ ├── UserDoesNotExist.ts │ │ │ ├── UserDomainEvent.ts │ │ │ ├── UserEmail.ts │ │ │ ├── UserFinder.ts │ │ │ ├── UserId.ts │ │ │ ├── UserName.ts │ │ │ ├── UserProfilePicture.ts │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ ├── UserRepository.ts │ │ │ └── UserStatus.ts │ │ │ └── infrastructure │ │ │ ├── InMemoryCacheUserRepository.ts │ │ │ └── MySqlUserRepository.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ └── RetentionUserCreator.ts │ │ │ └── increment_total_posts │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ └── domain │ │ │ ├── RetentionUser.ts │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ └── RetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── AggregateRoot.ts │ │ ├── Clock.ts │ │ ├── EmailAddress.ts │ │ ├── Identifier.ts │ │ ├── NumberValueObject.ts │ │ ├── StringValueObject.ts │ │ ├── criteria │ │ │ ├── Criteria.ts │ │ │ ├── Filter.ts │ │ │ ├── FilterField.ts │ │ │ ├── FilterOperator.ts │ │ │ ├── FilterValue.ts │ │ │ ├── Filters.ts │ │ │ ├── Order.ts │ │ │ ├── OrderBy.ts │ │ │ └── OrderType.ts │ │ └── event │ │ │ ├── DomainEvent.ts │ │ │ ├── DomainEventClass.ts │ │ │ ├── DomainEventSubscriber.ts │ │ │ └── EventBus.ts │ │ └── infrastructure │ │ ├── MariaDBConnection.ts │ │ ├── RedisClient.ts │ │ ├── criteria │ │ ├── CriteriaToSqlConverter.ts │ │ └── SearchParamsCriteriaFiltersParser.ts │ │ └── domain_event │ │ └── InMemoryEventBus.ts │ ├── tests │ └── contexts │ │ ├── mooc │ │ ├── courses_progress │ │ │ └── domain │ │ │ │ └── CourseProgressFinishedDomainEventMother.ts │ │ └── users │ │ │ ├── application │ │ │ ├── registrar │ │ │ │ └── UserRegistrar.test.ts │ │ │ └── search │ │ │ │ └── UserSearcher.test.ts │ │ │ ├── domain │ │ │ ├── DateMother.ts │ │ │ ├── UserEmailMother.ts │ │ │ ├── UserIdMother.ts │ │ │ ├── UserMother.ts │ │ │ ├── UserNameMother.ts │ │ │ ├── UserProfilePictureMother.ts │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ └── infrastructure │ │ │ ├── MockCoursesSuggestionLlm.ts │ │ │ ├── MockUserRepository.ts │ │ │ └── MySqlUserRepository.test.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ ├── increment_total_posts │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ ├── recalculate_average_post_likes │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ └── update_email │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ ├── domain │ │ │ └── RetentionUserMother.ts │ │ │ └── infrastructure │ │ │ └── MockRetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── EmailAddressMother.ts │ │ ├── EnumMother.ts │ │ └── criteria │ │ │ ├── CriteriaMother.ts │ │ │ ├── FilterMother.ts │ │ │ ├── FiltersMother.ts │ │ │ └── OrderMother.ts │ │ └── infrastructure │ │ ├── MockCache.ts │ │ ├── MockClock.ts │ │ ├── MockEventBus.ts │ │ └── criteria │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json ├── 07-good_practices ├── 2-implement_few_shot │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ │ └── codely.sql │ ├── etc │ │ └── request │ │ │ └── mooc │ │ │ ├── course_progress.http │ │ │ └── users.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── src │ │ ├── app │ │ │ ├── api │ │ │ │ └── mooc │ │ │ │ │ ├── course_progress │ │ │ │ │ └── route.ts │ │ │ │ │ └── users │ │ │ │ │ └── [user_id] │ │ │ │ │ └── route.ts │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ └── layout.tsx │ │ └── contexts │ │ │ ├── mooc │ │ │ ├── user_course_progress │ │ │ │ ├── application │ │ │ │ │ └── complete │ │ │ │ │ │ └── UserCourseProgressCompleter.ts │ │ │ │ └── domain │ │ │ │ │ └── UserCourseProgressCompletedDomainEvent.ts │ │ │ ├── user_course_suggestions │ │ │ │ ├── application │ │ │ │ │ ├── generate │ │ │ │ │ │ ├── GenerateUserCourseSuggestionsOnUserCourseProgressCompleted.ts │ │ │ │ │ │ └── UserCourseSuggestionsGenerator.ts │ │ │ │ │ └── search │ │ │ │ │ │ └── UserCourseSuggestionsSearcher.ts │ │ │ │ ├── domain │ │ │ │ │ ├── CourseSuggestionsGenerator.ts │ │ │ │ │ ├── UserCourseSuggestions.ts │ │ │ │ │ ├── UserCourseSuggestionsGenerated.ts │ │ │ │ │ └── UserCourseSuggestionsRepository.ts │ │ │ │ └── infrastructure │ │ │ │ │ ├── MySqlUserCourseSuggestionsRepository.ts │ │ │ │ │ ├── OllamaMistralCourseSuggestionsGenerator.ts │ │ │ │ │ └── OllamaMistralCourseSuggestionsGeneratorWithLengthExamples.ts │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── find │ │ │ │ │ └── UserFinder.ts │ │ │ │ ├── registrar │ │ │ │ │ └── UserRegistrar.ts │ │ │ │ └── update_course_suggestions │ │ │ │ │ ├── UpdateUserCourseSuggestionsOnUserCourseSuggestionsGenerated.ts │ │ │ │ │ └── UserCourseSuggestionsUpdater.ts │ │ │ │ ├── domain │ │ │ │ ├── CoursesSuggestionLlm.ts │ │ │ │ ├── User.ts │ │ │ │ ├── UserDoesNotExist.ts │ │ │ │ ├── UserDomainEvent.ts │ │ │ │ ├── UserEmail.ts │ │ │ │ ├── UserFinder.ts │ │ │ │ ├── UserId.ts │ │ │ │ ├── UserName.ts │ │ │ │ ├── UserProfilePicture.ts │ │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ │ ├── UserRepository.ts │ │ │ │ └── UserStatus.ts │ │ │ │ └── infrastructure │ │ │ │ ├── InMemoryCacheUserRepository.ts │ │ │ │ └── MySqlUserRepository.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ │ └── RetentionUserCreator.ts │ │ │ │ └── increment_total_posts │ │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ │ └── domain │ │ │ │ ├── RetentionUser.ts │ │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ │ └── RetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── AggregateRoot.ts │ │ │ ├── Clock.ts │ │ │ ├── EmailAddress.ts │ │ │ ├── Identifier.ts │ │ │ ├── NumberValueObject.ts │ │ │ ├── StringValueObject.ts │ │ │ ├── criteria │ │ │ │ ├── Criteria.ts │ │ │ │ ├── Filter.ts │ │ │ │ ├── FilterField.ts │ │ │ │ ├── FilterOperator.ts │ │ │ │ ├── FilterValue.ts │ │ │ │ ├── Filters.ts │ │ │ │ ├── Order.ts │ │ │ │ ├── OrderBy.ts │ │ │ │ └── OrderType.ts │ │ │ └── event │ │ │ │ ├── DomainEvent.ts │ │ │ │ ├── DomainEventClass.ts │ │ │ │ ├── DomainEventSubscriber.ts │ │ │ │ └── EventBus.ts │ │ │ └── infrastructure │ │ │ ├── MariaDBConnection.ts │ │ │ ├── RedisClient.ts │ │ │ ├── criteria │ │ │ ├── CriteriaToSqlConverter.ts │ │ │ └── SearchParamsCriteriaFiltersParser.ts │ │ │ └── domain_event │ │ │ └── InMemoryEventBus.ts │ ├── tests │ │ └── contexts │ │ │ ├── mooc │ │ │ ├── courses_progress │ │ │ │ └── domain │ │ │ │ │ └── CourseProgressFinishedDomainEventMother.ts │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── registrar │ │ │ │ │ └── UserRegistrar.test.ts │ │ │ │ └── search │ │ │ │ │ └── UserSearcher.test.ts │ │ │ │ ├── domain │ │ │ │ ├── DateMother.ts │ │ │ │ ├── UserEmailMother.ts │ │ │ │ ├── UserIdMother.ts │ │ │ │ ├── UserMother.ts │ │ │ │ ├── UserNameMother.ts │ │ │ │ ├── UserProfilePictureMother.ts │ │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ │ └── infrastructure │ │ │ │ ├── MockCoursesSuggestionLlm.ts │ │ │ │ ├── MockUserRepository.ts │ │ │ │ └── MySqlUserRepository.test.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ │ ├── increment_total_posts │ │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ │ ├── recalculate_average_post_likes │ │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ │ └── update_email │ │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ │ ├── domain │ │ │ │ └── RetentionUserMother.ts │ │ │ │ └── infrastructure │ │ │ │ └── MockRetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── EmailAddressMother.ts │ │ │ ├── EnumMother.ts │ │ │ └── criteria │ │ │ │ ├── CriteriaMother.ts │ │ │ │ ├── FilterMother.ts │ │ │ │ ├── FiltersMother.ts │ │ │ │ └── OrderMother.ts │ │ │ └── infrastructure │ │ │ ├── MockCache.ts │ │ │ ├── MockClock.ts │ │ │ ├── MockEventBus.ts │ │ │ └── criteria │ │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json └── 3-add_types │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ └── codely.sql │ ├── etc │ └── request │ │ └── mooc │ │ ├── course_progress.http │ │ └── users.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── next.svg │ └── vercel.svg │ ├── src │ ├── app │ │ ├── api │ │ │ └── mooc │ │ │ │ ├── course_progress │ │ │ │ └── route.ts │ │ │ │ └── users │ │ │ │ └── [user_id] │ │ │ │ └── route.ts │ │ ├── favicon.ico │ │ ├── globals.css │ │ └── layout.tsx │ └── contexts │ │ ├── mooc │ │ ├── user_course_progress │ │ │ ├── application │ │ │ │ └── complete │ │ │ │ │ └── UserCourseProgressCompleter.ts │ │ │ └── domain │ │ │ │ └── UserCourseProgressCompletedDomainEvent.ts │ │ ├── user_course_suggestions │ │ │ ├── application │ │ │ │ ├── generate │ │ │ │ │ ├── GenerateUserCourseSuggestionsOnUserCourseProgressCompleted.ts │ │ │ │ │ └── UserCourseSuggestionsGenerator.ts │ │ │ │ └── search │ │ │ │ │ └── UserCourseSuggestionsSearcher.ts │ │ │ ├── domain │ │ │ │ ├── CourseSuggestion.ts │ │ │ │ ├── CourseSuggestionsGenerator.ts │ │ │ │ ├── UserCourseSuggestions.ts │ │ │ │ ├── UserCourseSuggestionsGenerated.ts │ │ │ │ └── UserCourseSuggestionsRepository.ts │ │ │ └── infrastructure │ │ │ │ ├── MySqlUserCourseSuggestionsRepository.ts │ │ │ │ ├── OllamaMistralCourseSuggestionsGenerator.ts │ │ │ │ └── OpenAIChatGPT35CourseSuggestionsGenerator.ts │ │ └── users │ │ │ ├── application │ │ │ ├── find │ │ │ │ └── UserFinder.ts │ │ │ ├── registrar │ │ │ │ └── UserRegistrar.ts │ │ │ └── update_course_suggestions │ │ │ │ ├── UpdateUserCourseSuggestionsOnUserCourseSuggestionsGenerated.ts │ │ │ │ └── UserCourseSuggestionsUpdater.ts │ │ │ ├── domain │ │ │ ├── CoursesSuggestionLlm.ts │ │ │ ├── User.ts │ │ │ ├── UserDoesNotExist.ts │ │ │ ├── UserDomainEvent.ts │ │ │ ├── UserEmail.ts │ │ │ ├── UserFinder.ts │ │ │ ├── UserId.ts │ │ │ ├── UserName.ts │ │ │ ├── UserProfilePicture.ts │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ ├── UserRepository.ts │ │ │ └── UserStatus.ts │ │ │ └── infrastructure │ │ │ ├── InMemoryCacheUserRepository.ts │ │ │ └── MySqlUserRepository.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ └── RetentionUserCreator.ts │ │ │ └── increment_total_posts │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ └── domain │ │ │ ├── RetentionUser.ts │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ └── RetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── AggregateRoot.ts │ │ ├── Clock.ts │ │ ├── EmailAddress.ts │ │ ├── Identifier.ts │ │ ├── NumberValueObject.ts │ │ ├── StringValueObject.ts │ │ ├── criteria │ │ │ ├── Criteria.ts │ │ │ ├── Filter.ts │ │ │ ├── FilterField.ts │ │ │ ├── FilterOperator.ts │ │ │ ├── FilterValue.ts │ │ │ ├── Filters.ts │ │ │ ├── Order.ts │ │ │ ├── OrderBy.ts │ │ │ └── OrderType.ts │ │ └── event │ │ │ ├── DomainEvent.ts │ │ │ ├── DomainEventClass.ts │ │ │ ├── DomainEventSubscriber.ts │ │ │ └── EventBus.ts │ │ └── infrastructure │ │ ├── MariaDBConnection.ts │ │ ├── RedisClient.ts │ │ ├── criteria │ │ ├── CriteriaToSqlConverter.ts │ │ └── SearchParamsCriteriaFiltersParser.ts │ │ └── domain_event │ │ └── InMemoryEventBus.ts │ ├── tests │ └── contexts │ │ ├── mooc │ │ ├── courses_progress │ │ │ └── domain │ │ │ │ └── CourseProgressFinishedDomainEventMother.ts │ │ └── users │ │ │ ├── application │ │ │ ├── registrar │ │ │ │ └── UserRegistrar.test.ts │ │ │ └── search │ │ │ │ └── UserSearcher.test.ts │ │ │ ├── domain │ │ │ ├── DateMother.ts │ │ │ ├── UserEmailMother.ts │ │ │ ├── UserIdMother.ts │ │ │ ├── UserMother.ts │ │ │ ├── UserNameMother.ts │ │ │ ├── UserProfilePictureMother.ts │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ └── infrastructure │ │ │ ├── MockCoursesSuggestionLlm.ts │ │ │ ├── MockUserRepository.ts │ │ │ └── MySqlUserRepository.test.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ ├── create │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ ├── increment_total_posts │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ ├── recalculate_average_post_likes │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ └── update_email │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ ├── domain │ │ │ └── RetentionUserMother.ts │ │ │ └── infrastructure │ │ │ └── MockRetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── EmailAddressMother.ts │ │ ├── EnumMother.ts │ │ └── criteria │ │ │ ├── CriteriaMother.ts │ │ │ ├── FilterMother.ts │ │ │ ├── FiltersMother.ts │ │ │ └── OrderMother.ts │ │ └── infrastructure │ │ ├── MockCache.ts │ │ ├── MockClock.ts │ │ ├── MockEventBus.ts │ │ └── criteria │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json ├── 08-testing ├── 1-debug_prompt │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ │ └── codely.sql │ ├── etc │ │ └── request │ │ │ └── mooc │ │ │ ├── course_progress.http │ │ │ └── users.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── src │ │ ├── app │ │ │ ├── api │ │ │ │ └── mooc │ │ │ │ │ ├── course_progress │ │ │ │ │ └── route.ts │ │ │ │ │ └── users │ │ │ │ │ └── [user_id] │ │ │ │ │ └── route.ts │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ └── layout.tsx │ │ └── contexts │ │ │ ├── mooc │ │ │ ├── user_course_progress │ │ │ │ ├── application │ │ │ │ │ └── complete │ │ │ │ │ │ └── UserCourseProgressCompleter.ts │ │ │ │ └── domain │ │ │ │ │ └── UserCourseProgressCompletedDomainEvent.ts │ │ │ ├── user_course_suggestions │ │ │ │ ├── application │ │ │ │ │ ├── generate │ │ │ │ │ │ ├── GenerateUserCourseSuggestionsOnUserCourseProgressCompleted.ts │ │ │ │ │ │ └── UserCourseSuggestionsGenerator.ts │ │ │ │ │ └── search │ │ │ │ │ │ └── UserCourseSuggestionsSearcher.ts │ │ │ │ ├── domain │ │ │ │ │ ├── CourseSuggestionsGenerator.ts │ │ │ │ │ ├── UserCourseSuggestions.ts │ │ │ │ │ ├── UserCourseSuggestionsGenerated.ts │ │ │ │ │ └── UserCourseSuggestionsRepository.ts │ │ │ │ └── infrastructure │ │ │ │ │ ├── MySqlUserCourseSuggestionsRepository.ts │ │ │ │ │ ├── OllamaMistralCourseSuggestionsGenerator.ts │ │ │ │ │ └── OllamaMistralCourseSuggestionsGeneratorWithLengthExamples.ts │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── find │ │ │ │ │ └── UserFinder.ts │ │ │ │ ├── registrar │ │ │ │ │ └── UserRegistrar.ts │ │ │ │ └── update_course_suggestions │ │ │ │ │ ├── UpdateUserCourseSuggestionsOnUserCourseSuggestionsGenerated.ts │ │ │ │ │ └── UserCourseSuggestionsUpdater.ts │ │ │ │ ├── domain │ │ │ │ ├── CoursesSuggestionLlm.ts │ │ │ │ ├── User.ts │ │ │ │ ├── UserDoesNotExist.ts │ │ │ │ ├── UserDomainEvent.ts │ │ │ │ ├── UserEmail.ts │ │ │ │ ├── UserFinder.ts │ │ │ │ ├── UserId.ts │ │ │ │ ├── UserName.ts │ │ │ │ ├── UserProfilePicture.ts │ │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ │ ├── UserRepository.ts │ │ │ │ └── UserStatus.ts │ │ │ │ └── infrastructure │ │ │ │ ├── InMemoryCacheUserRepository.ts │ │ │ │ └── MySqlUserRepository.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ │ └── RetentionUserCreator.ts │ │ │ │ └── increment_total_posts │ │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ │ └── domain │ │ │ │ ├── RetentionUser.ts │ │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ │ └── RetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── AggregateRoot.ts │ │ │ ├── Clock.ts │ │ │ ├── EmailAddress.ts │ │ │ ├── Identifier.ts │ │ │ ├── NumberValueObject.ts │ │ │ ├── StringValueObject.ts │ │ │ ├── criteria │ │ │ │ ├── Criteria.ts │ │ │ │ ├── Filter.ts │ │ │ │ ├── FilterField.ts │ │ │ │ ├── FilterOperator.ts │ │ │ │ ├── FilterValue.ts │ │ │ │ ├── Filters.ts │ │ │ │ ├── Order.ts │ │ │ │ ├── OrderBy.ts │ │ │ │ └── OrderType.ts │ │ │ └── event │ │ │ │ ├── DomainEvent.ts │ │ │ │ ├── DomainEventClass.ts │ │ │ │ ├── DomainEventSubscriber.ts │ │ │ │ └── EventBus.ts │ │ │ └── infrastructure │ │ │ ├── MariaDBConnection.ts │ │ │ ├── RedisClient.ts │ │ │ ├── criteria │ │ │ ├── CriteriaToSqlConverter.ts │ │ │ └── SearchParamsCriteriaFiltersParser.ts │ │ │ └── domain_event │ │ │ └── InMemoryEventBus.ts │ ├── tests │ │ └── contexts │ │ │ ├── mooc │ │ │ ├── courses_progress │ │ │ │ └── domain │ │ │ │ │ └── CourseProgressFinishedDomainEventMother.ts │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── registrar │ │ │ │ │ └── UserRegistrar.test.ts │ │ │ │ └── search │ │ │ │ │ └── UserSearcher.test.ts │ │ │ │ ├── domain │ │ │ │ ├── DateMother.ts │ │ │ │ ├── UserEmailMother.ts │ │ │ │ ├── UserIdMother.ts │ │ │ │ ├── UserMother.ts │ │ │ │ ├── UserNameMother.ts │ │ │ │ ├── UserProfilePictureMother.ts │ │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ │ └── infrastructure │ │ │ │ ├── MockCoursesSuggestionLlm.ts │ │ │ │ ├── MockUserRepository.ts │ │ │ │ └── MySqlUserRepository.test.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ │ ├── increment_total_posts │ │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ │ ├── recalculate_average_post_likes │ │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ │ └── update_email │ │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ │ ├── domain │ │ │ │ └── RetentionUserMother.ts │ │ │ │ └── infrastructure │ │ │ │ └── MockRetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── EmailAddressMother.ts │ │ │ ├── EnumMother.ts │ │ │ └── criteria │ │ │ │ ├── CriteriaMother.ts │ │ │ │ ├── FilterMother.ts │ │ │ │ ├── FiltersMother.ts │ │ │ │ └── OrderMother.ts │ │ │ └── infrastructure │ │ │ ├── MockCache.ts │ │ │ ├── MockClock.ts │ │ │ ├── MockEventBus.ts │ │ │ └── criteria │ │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json ├── 2-integration_tests │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ │ └── codely.sql │ ├── etc │ │ └── request │ │ │ └── mooc │ │ │ ├── course_progress.http │ │ │ └── users.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── src │ │ ├── app │ │ │ ├── api │ │ │ │ └── mooc │ │ │ │ │ ├── course_progress │ │ │ │ │ └── route.ts │ │ │ │ │ └── users │ │ │ │ │ └── [user_id] │ │ │ │ │ └── route.ts │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ └── layout.tsx │ │ └── contexts │ │ │ ├── mooc │ │ │ ├── user_course_progress │ │ │ │ ├── application │ │ │ │ │ └── complete │ │ │ │ │ │ └── UserCourseProgressCompleter.ts │ │ │ │ └── domain │ │ │ │ │ └── UserCourseProgressCompletedDomainEvent.ts │ │ │ ├── user_course_suggestions │ │ │ │ ├── application │ │ │ │ │ ├── generate │ │ │ │ │ │ ├── GenerateUserCourseSuggestionsOnUserCourseProgressCompleted.ts │ │ │ │ │ │ └── UserCourseSuggestionsGenerator.ts │ │ │ │ │ └── search │ │ │ │ │ │ └── UserCourseSuggestionsSearcher.ts │ │ │ │ ├── domain │ │ │ │ │ ├── CourseSuggestion.ts │ │ │ │ │ ├── CourseSuggestionsGenerator.ts │ │ │ │ │ ├── UserCourseSuggestions.ts │ │ │ │ │ ├── UserCourseSuggestionsGenerated.ts │ │ │ │ │ └── UserCourseSuggestionsRepository.ts │ │ │ │ └── infrastructure │ │ │ │ │ ├── MySqlUserCourseSuggestionsRepository.ts │ │ │ │ │ ├── OllamaMistralCourseSuggestionsGenerator.ts │ │ │ │ │ └── OpenAIChatGPT35CourseSuggestionsGenerator.ts │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── find │ │ │ │ │ └── UserFinder.ts │ │ │ │ ├── registrar │ │ │ │ │ └── UserRegistrar.ts │ │ │ │ └── update_course_suggestions │ │ │ │ │ ├── UpdateUserCourseSuggestionsOnUserCourseSuggestionsGenerated.ts │ │ │ │ │ └── UserCourseSuggestionsUpdater.ts │ │ │ │ ├── domain │ │ │ │ ├── CoursesSuggestionLlm.ts │ │ │ │ ├── User.ts │ │ │ │ ├── UserDoesNotExist.ts │ │ │ │ ├── UserDomainEvent.ts │ │ │ │ ├── UserEmail.ts │ │ │ │ ├── UserFinder.ts │ │ │ │ ├── UserId.ts │ │ │ │ ├── UserName.ts │ │ │ │ ├── UserProfilePicture.ts │ │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ │ ├── UserRepository.ts │ │ │ │ └── UserStatus.ts │ │ │ │ └── infrastructure │ │ │ │ ├── InMemoryCacheUserRepository.ts │ │ │ │ └── MySqlUserRepository.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ │ └── RetentionUserCreator.ts │ │ │ │ └── increment_total_posts │ │ │ │ │ ├── IncrementRetentionUserTotalPostsOnPostPublished.ts │ │ │ │ │ └── RetentionUserTotalPostsIncrementer.ts │ │ │ │ └── domain │ │ │ │ ├── RetentionUser.ts │ │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ │ └── RetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── AggregateRoot.ts │ │ │ ├── Clock.ts │ │ │ ├── EmailAddress.ts │ │ │ ├── Identifier.ts │ │ │ ├── NumberValueObject.ts │ │ │ ├── StringValueObject.ts │ │ │ ├── criteria │ │ │ │ ├── Criteria.ts │ │ │ │ ├── Filter.ts │ │ │ │ ├── FilterField.ts │ │ │ │ ├── FilterOperator.ts │ │ │ │ ├── FilterValue.ts │ │ │ │ ├── Filters.ts │ │ │ │ ├── Order.ts │ │ │ │ ├── OrderBy.ts │ │ │ │ └── OrderType.ts │ │ │ └── event │ │ │ │ ├── DomainEvent.ts │ │ │ │ ├── DomainEventClass.ts │ │ │ │ ├── DomainEventSubscriber.ts │ │ │ │ └── EventBus.ts │ │ │ └── infrastructure │ │ │ ├── MariaDBConnection.ts │ │ │ ├── RedisClient.ts │ │ │ ├── criteria │ │ │ ├── CriteriaToSqlConverter.ts │ │ │ └── SearchParamsCriteriaFiltersParser.ts │ │ │ └── domain_event │ │ │ └── InMemoryEventBus.ts │ ├── tests │ │ └── contexts │ │ │ ├── mooc │ │ │ ├── courses_progress │ │ │ │ └── domain │ │ │ │ │ └── CourseProgressFinishedDomainEventMother.ts │ │ │ ├── user_course_suggestions │ │ │ │ ├── domain │ │ │ │ │ ├── CourseSuggestionMother.ts │ │ │ │ │ └── UserCourseSuggestionsMother.ts │ │ │ │ └── infrastructure │ │ │ │ │ └── OllamaMistralCourseSuggestionsGenerator.test.ts │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── registrar │ │ │ │ │ └── UserRegistrar.test.ts │ │ │ │ └── search │ │ │ │ │ └── UserSearcher.test.ts │ │ │ │ ├── domain │ │ │ │ ├── DateMother.ts │ │ │ │ ├── UserEmailMother.ts │ │ │ │ ├── UserIdMother.ts │ │ │ │ ├── UserMother.ts │ │ │ │ ├── UserNameMother.ts │ │ │ │ ├── UserProfilePictureMother.ts │ │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ │ └── infrastructure │ │ │ │ ├── MockCoursesSuggestionLlm.ts │ │ │ │ ├── MockUserRepository.ts │ │ │ │ └── MySqlUserRepository.test.ts │ │ │ ├── retention │ │ │ └── users │ │ │ │ ├── application │ │ │ │ ├── create │ │ │ │ │ └── CreateRetentionUserOnUserRegistered.test.ts │ │ │ │ ├── increment_total_posts │ │ │ │ │ └── IncrementRetentionUserTotalPostsOnPostPublished.test.ts │ │ │ │ ├── recalculate_average_post_likes │ │ │ │ │ └── RecalculateRetentionUserAveragePostLikesOnPostLiked.test.ts │ │ │ │ └── update_email │ │ │ │ │ └── UpdateRetentionUserEmailOnUserEmailUpdated.test.ts │ │ │ │ ├── domain │ │ │ │ └── RetentionUserMother.ts │ │ │ │ └── infrastructure │ │ │ │ └── MockRetentionUserRepository.ts │ │ │ └── shared │ │ │ ├── domain │ │ │ ├── EmailAddressMother.ts │ │ │ ├── EnumMother.ts │ │ │ └── criteria │ │ │ │ ├── CriteriaMother.ts │ │ │ │ ├── FilterMother.ts │ │ │ │ ├── FiltersMother.ts │ │ │ │ └── OrderMother.ts │ │ │ └── infrastructure │ │ │ ├── MockCache.ts │ │ │ ├── MockClock.ts │ │ │ ├── MockEventBus.ts │ │ │ └── criteria │ │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json └── 3-llm_ci │ ├── .eslintrc.json │ ├── .github │ └── workflows │ │ └── ci.yml │ ├── .gitignore │ ├── README.md │ ├── compose.yml │ ├── databases │ └── codely.sql │ ├── etc │ └── request │ │ └── mooc │ │ ├── course_progress.http │ │ └── users.http │ ├── jest.config.js │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── next.svg │ └── vercel.svg │ ├── src │ ├── app │ │ ├── api │ │ │ └── mooc │ │ │ │ ├── course_progress │ │ │ │ └── route.ts │ │ │ │ └── users │ │ │ │ └── [user_id] │ │ │ │ └── route.ts │ │ ├── favicon.ico │ │ ├── globals.css │ │ └── layout.tsx │ └── contexts │ │ ├── mooc │ │ ├── user_course_progress │ │ │ ├── application │ │ │ │ └── complete │ │ │ │ │ └── UserCourseProgressCompleter.ts │ │ │ └── domain │ │ │ │ └── UserCourseProgressCompletedDomainEvent.ts │ │ ├── user_course_suggestions │ │ │ ├── application │ │ │ │ ├── generate │ │ │ │ │ ├── GenerateUserCourseSuggestionsOnUserCourseProgressCompleted.ts │ │ │ │ │ └── UserCourseSuggestionsGenerator.ts │ │ │ │ └── search │ │ │ │ │ └── UserCourseSuggestionsSearcher.ts │ │ │ ├── domain │ │ │ │ ├── CourseSuggestion.ts │ │ │ │ ├── CourseSuggestionsGenerator.ts │ │ │ │ ├── UserCourseSuggestions.ts │ │ │ │ ├── UserCourseSuggestionsGenerated.ts │ │ │ │ └── UserCourseSuggestionsRepository.ts │ │ │ └── infrastructure │ │ │ │ ├── MySqlUserCourseSuggestionsRepository.ts │ │ │ │ ├── OllamaMistralCourseSuggestionsGenerator.ts │ │ │ │ └── OpenAIChatGPT35CourseSuggestionsGenerator.ts │ │ └── users │ │ │ ├── application │ │ │ ├── find │ │ │ │ └── UserFinder.ts │ │ │ ├── registrar │ │ │ │ └── UserRegistrar.ts │ │ │ └── update_course_suggestions │ │ │ │ ├── UpdateUserCourseSuggestionsOnUserCourseSuggestionsGenerated.ts │ │ │ │ └── UserCourseSuggestionsUpdater.ts │ │ │ ├── domain │ │ │ ├── CoursesSuggestionLlm.ts │ │ │ ├── User.ts │ │ │ ├── UserDoesNotExist.ts │ │ │ ├── UserDomainEvent.ts │ │ │ ├── UserEmail.ts │ │ │ ├── UserFinder.ts │ │ │ ├── UserId.ts │ │ │ ├── UserName.ts │ │ │ ├── UserProfilePicture.ts │ │ │ ├── UserRegisteredDomainEvent.ts │ │ │ ├── UserRepository.ts │ │ │ └── UserStatus.ts │ │ │ └── infrastructure │ │ │ ├── InMemoryCacheUserRepository.ts │ │ │ └── MySqlUserRepository.ts │ │ ├── retention │ │ └── users │ │ │ ├── application │ │ │ └── create │ │ │ │ ├── CreateRetentionUserOnUserRegistered.ts │ │ │ │ └── RetentionUserCreator.ts │ │ │ └── domain │ │ │ ├── RetentionUser.ts │ │ │ ├── RetentionUserDoesNotExist.ts │ │ │ └── RetentionUserRepository.ts │ │ └── shared │ │ ├── domain │ │ ├── AggregateRoot.ts │ │ ├── Clock.ts │ │ ├── EmailAddress.ts │ │ ├── Identifier.ts │ │ ├── NumberValueObject.ts │ │ ├── StringValueObject.ts │ │ ├── criteria │ │ │ ├── Criteria.ts │ │ │ ├── Filter.ts │ │ │ ├── FilterField.ts │ │ │ ├── FilterOperator.ts │ │ │ ├── FilterValue.ts │ │ │ ├── Filters.ts │ │ │ ├── Order.ts │ │ │ ├── OrderBy.ts │ │ │ └── OrderType.ts │ │ └── event │ │ │ ├── DomainEvent.ts │ │ │ ├── DomainEventClass.ts │ │ │ ├── DomainEventSubscriber.ts │ │ │ └── EventBus.ts │ │ └── infrastructure │ │ ├── MariaDBConnection.ts │ │ ├── RedisClient.ts │ │ ├── criteria │ │ ├── CriteriaToSqlConverter.ts │ │ └── SearchParamsCriteriaFiltersParser.ts │ │ └── domain_event │ │ └── InMemoryEventBus.ts │ ├── tests │ └── contexts │ │ ├── mooc │ │ ├── courses_progress │ │ │ └── domain │ │ │ │ └── CourseProgressFinishedDomainEventMother.ts │ │ ├── user_course_suggestions │ │ │ ├── domain │ │ │ │ ├── CourseSuggestionMother.ts │ │ │ │ └── UserCourseSuggestionsMother.ts │ │ │ └── infrastructure │ │ │ │ ├── OllamaMistralCourseSuggestionsGenerator.test.ts │ │ │ │ └── OpenAIChatGPT35CourseSuggestionsGenerator.ci.test.ts │ │ └── users │ │ │ ├── application │ │ │ └── registrar │ │ │ │ └── UserRegistrar.test.ts │ │ │ ├── domain │ │ │ ├── DateMother.ts │ │ │ ├── UserEmailMother.ts │ │ │ ├── UserIdMother.ts │ │ │ ├── UserMother.ts │ │ │ ├── UserNameMother.ts │ │ │ ├── UserProfilePictureMother.ts │ │ │ └── UserRegisteredDomainEventMother.ts │ │ │ └── infrastructure │ │ │ ├── MockCoursesSuggestionLlm.ts │ │ │ ├── MockUserRepository.ts │ │ │ └── MySqlUserRepository.test.ts │ │ └── shared │ │ ├── domain │ │ ├── EmailAddressMother.ts │ │ ├── EnumMother.ts │ │ └── criteria │ │ │ ├── CriteriaMother.ts │ │ │ ├── FilterMother.ts │ │ │ ├── FiltersMother.ts │ │ │ └── OrderMother.ts │ │ └── infrastructure │ │ ├── MockClock.ts │ │ ├── MockEventBus.ts │ │ └── criteria │ │ └── CriteriaToSqlConverter.test.ts │ └── tsconfig.json ├── LICENSE └── README.md /01-intro/2-ollama/2-commands.sh: -------------------------------------------------------------------------------- 1 | ollama pull gemma:latest 2 | ollama run gemma:latest 3 | -------------------------------------------------------------------------------- /01-intro/2-ollama/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | ollama: 3 | image: ollama/ollama 4 | container_name: ollama 5 | volumes: 6 | - ~/.ollama:/root/.ollama 7 | ports: 8 | - "11434:11434" 9 | volumes: 10 | ollama: 11 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/databases/ecommerce.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | RRSS CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE rrss__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | interests TEXT 12 | ); 13 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/etc/request/retention/course_suggestions.http: -------------------------------------------------------------------------------- 1 | ### Filtering by one field 2 | GET http://localhost:3000/api/retention/course_suggestions/7e54a4ce-1abd-4db1-b0d2-ee6178f98df0 3 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/01-intro/3-integrate_model/src/app/favicon.ico -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/rrss/post_likes/domain/PostLikeIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { PostLikeId } from "../../../../../src/contexts/rrss/post_likes/domain/PostLikeId"; 4 | 5 | export class PostLikeIdMother { 6 | static create(value?: string): PostLikeId { 7 | return new PostLikeId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/rrss/posts/domain/PostContentMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { PostContent } from "../../../../../src/contexts/rrss/posts/domain/PostContent"; 4 | 5 | export class PostContentMother { 6 | static create(value?: string): PostContent { 7 | return new PostContent(value ?? faker.string.alpha()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/rrss/posts/domain/PostIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { PostId } from "../../../../../src/contexts/rrss/posts/domain/PostId"; 4 | 5 | export class PostIdMother { 6 | static create(value?: string): PostId { 7 | return new PostId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/rrss/posts/domain/PostLikesMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { PostTotalLikes } from "../../../../../src/contexts/rrss/posts/domain/PostTotalLikes"; 4 | 5 | export class PostLikesMother { 6 | static create(value?: number): PostTotalLikes { 7 | return new PostTotalLikes(value ?? faker.number.int({ min: 0, max: 10000000 })); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/rrss/users/domain/DateMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | export class DateMother { 4 | static create(value?: string): Date { 5 | return new Date(value ?? faker.date.recent()); 6 | } 7 | 8 | static today(): Date { 9 | return new Date(); 10 | } 11 | 12 | static yesterday(): Date { 13 | const today = new Date(); 14 | const yesterday = new Date(today); 15 | yesterday.setDate(today.getDate() - 1); 16 | 17 | return yesterday; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/rrss/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/rrss/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/rrss/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/rrss/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/rrss/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/rrss/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/rrss/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/rrss/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/shared/domain/EnumMother.ts: -------------------------------------------------------------------------------- 1 | export class EnumMother { 2 | static randomFrom(anEnum: T): T[keyof T] { 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | // @ts-ignore 5 | const enumValues = Object.keys(anEnum).filter((key) => 6 | isNaN(Number(key)), 7 | ) as unknown as T[keyof T][]; 8 | const randomIndex = Math.floor(Math.random() * enumValues.length); 9 | 10 | return anEnum[enumValues[randomIndex] as keyof typeof anEnum]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /01-intro/3-integrate_model/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | completed_courses TEXT 12 | ); 13 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### GET a user 2 | GET http://localhost:3000/api/mooc/users/5353ef23-e982-4acc-928e-c7cdad92152a 3 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/02-software_architecture/2-implementation_details/src/app/favicon.ico -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/mooc/user_course_suggestions/domain/UserCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../users/domain/UserId"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface UserCourseSuggestionsRepository { 5 | search(userId: UserId): Promise; 6 | } 7 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /02-software_architecture/2-implementation_details/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | finished_courses TEXT 12 | ); 13 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### Filtering by one field 2 | GET http://localhost:3000/api/mooc/users/efc891f8-3c87-40ac-82c4-2a4fe2baabfd 3 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/etc/request/retention/course_suggestions.http: -------------------------------------------------------------------------------- 1 | ### Filtering by one field 2 | GET http://localhost:3000/api/retention/course_suggestions/7e54a4ce-1abd-4db1-b0d2-ee6178f98df0 3 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/03-user_profile_wth_suggestions/1-llm_use_case/src/app/favicon.ico -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/mooc/course_suggestions/domain/CourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | export interface CourseSuggestionsRepository { 2 | byFinishedCourses(finishedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/mooc/course_suggestions/infrastructure/OllamaMistralCoursesSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | import { CourseSuggestionsRepository } from "../domain/CourseSuggestionsRepository"; 2 | 3 | export class OllamaMistralCoursesSuggestionsRepository implements CourseSuggestionsRepository { 4 | async byFinishedCourses(_finishedCourses: string[]): Promise { 5 | return Promise.resolve(""); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/1-llm_use_case/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | finished_courses TEXT 12 | ); 13 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### Filtering by one field 2 | GET http://localhost:3000/api/mooc/users/efc891f8-3c87-40ac-82c4-2a4fe2baabfd 3 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/etc/request/retention/course_suggestions.http: -------------------------------------------------------------------------------- 1 | ### Filtering by one field 2 | GET http://localhost:3000/api/retention/course_suggestions/7e54a4ce-1abd-4db1-b0d2-ee6178f98df0 3 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/03-user_profile_wth_suggestions/2-llm_in_repo/src/app/favicon.ico -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/mooc/course_suggestions/domain/CourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | export interface CourseSuggestionsRepository { 2 | byFinishedCourses(finishedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/mooc/users/infrastructure/OllamaMistralCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | import { CourseSuggestionsRepository } from "../../course_suggestions/domain/CourseSuggestionsRepository"; 2 | 3 | export class OllamaMistralCourseSuggestionsRepository implements CourseSuggestionsRepository { 4 | async byFinishedCourses(_finishedCourses: string[]): Promise { 5 | return Promise.resolve(""); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /03-user_profile_wth_suggestions/2-llm_in_repo/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | completed_courses TEXT 12 | ); 13 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### GET a user 2 | GET http://localhost:3000/api/mooc/users/5353ef23-e982-4acc-928e-c7cdad92152a 3 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/04-llm_implementation_details/1-implement_a_recommendator/src/app/favicon.ico -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/mooc/user_course_suggestions/domain/UserCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | export interface UserCourseSuggestionsRepository { 2 | byCompletedCourses(finishedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/1-implement_a_recommendator/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | completed_courses TEXT, 12 | suggested_courses TEXT 13 | ); 14 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### GET a user 2 | GET http://localhost:3000/api/mooc/users/5353ef23-e982-4acc-928e-c7cdad92152a 3 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/04-llm_implementation_details/2-cache/src/app/favicon.ico -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/mooc/user_course_suggestions/domain/UserCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | export interface UserCourseSuggestionsRepository { 2 | byCompletedCourses(completedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "ACTIVE", 3 | Archived = "ARCHIVED", 4 | } 5 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/tests/contexts/shared/domain/EnumMother.ts: -------------------------------------------------------------------------------- 1 | export class EnumMother { 2 | static randomFrom(anEnum: T): T[keyof T] { 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | // @ts-ignore 5 | const enumValues = Object.keys(anEnum).filter((key) => 6 | isNaN(Number(key)), 7 | ) as unknown as T[keyof T][]; 8 | const randomIndex = Math.floor(Math.random() * enumValues.length); 9 | 10 | return anEnum[enumValues[randomIndex] as keyof typeof anEnum]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /04-llm_implementation_details/2-cache/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | suggested_courses TEXT 12 | ); 13 | 14 | CREATE TABLE mooc__user_course_suggestions ( 15 | user_id UUID PRIMARY KEY, 16 | completed_courses TEXT, 17 | suggested_courses TEXT 18 | ); 19 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### GET a user 2 | GET http://localhost:3000/api/mooc/users/5353ef23-e982-4acc-928e-c7cdad92152a 3 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/etc/request/retention/course_suggestions.http: -------------------------------------------------------------------------------- 1 | ### Filtering by one field 2 | GET http://localhost:3000/api/retention/course_suggestions/7e54a4ce-1abd-4db1-b0d2-ee6178f98df0 3 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/05-event_driven/2-llm_with_domain_events/src/app/favicon.ico -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/mooc/user_course_suggestions/domain/CourseSuggestionsGenerator.ts: -------------------------------------------------------------------------------- 1 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 2 | 3 | export interface CourseSuggestionsGenerator { 4 | generate(userCourseSuggestions: UserCourseSuggestions): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/mooc/user_course_suggestions/domain/UserCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../users/domain/UserId"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface UserCourseSuggestionsRepository { 5 | save(suggestions: UserCourseSuggestions): Promise; 6 | 7 | search(userId: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/mooc/users/domain/CoursesSuggestionLlm.ts: -------------------------------------------------------------------------------- 1 | export interface CoursesSuggestionLlm { 2 | predict(finishedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /05-event_driven/2-llm_with_domain_events/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 06_1_ia_good_practices_mariadb: 3 | image: mariadb:11 4 | restart: always 5 | environment: 6 | MYSQL_ROOT_PASSWORD: adminpassword 7 | MYSQL_DATABASE: mooc 8 | MYSQL_USER: codely 9 | MYSQL_PASSWORD: c0d3ly7v 10 | TZ: UTC 11 | ports: 12 | - "3306:3306" 13 | volumes: 14 | - db_data:/var/lib/mysql 15 | - ./databases/codely.sql:/docker-entrypoint-initdb.d/init.sql 16 | 17 | volumes: 18 | db_data: 19 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | suggested_courses TEXT 12 | ); 13 | 14 | CREATE TABLE mooc__user_course_suggestions ( 15 | user_id UUID PRIMARY KEY, 16 | completed_courses TEXT, 17 | suggested_courses TEXT 18 | ); 19 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### GET a user 2 | GET http://localhost:3000/api/mooc/users/5353ef23-e982-4acc-928e-c7cdad92152a 3 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/06-chatgpt/1-add_chatgpt/src/app/favicon.ico -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/mooc/user_course_suggestions/domain/CourseSuggestionsGenerator.ts: -------------------------------------------------------------------------------- 1 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 2 | 3 | export interface CourseSuggestionsGenerator { 4 | generate(userCourseSuggestions: UserCourseSuggestions): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/mooc/user_course_suggestions/domain/UserCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../users/domain/UserId"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface UserCourseSuggestionsRepository { 5 | save(suggestions: UserCourseSuggestions): Promise; 6 | 7 | search(userId: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/mooc/users/domain/CoursesSuggestionLlm.ts: -------------------------------------------------------------------------------- 1 | export interface CoursesSuggestionLlm { 2 | predict(finishedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/tests/contexts/mooc/users/domain/DateMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | export class DateMother { 4 | static create(value?: string): Date { 5 | return new Date(value ?? faker.date.recent()); 6 | } 7 | 8 | static today(): Date { 9 | return new Date(); 10 | } 11 | 12 | static yesterday(): Date { 13 | const today = new Date(); 14 | const yesterday = new Date(today); 15 | yesterday.setDate(today.getDate() - 1); 16 | 17 | return yesterday; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/tests/contexts/shared/domain/EnumMother.ts: -------------------------------------------------------------------------------- 1 | export class EnumMother { 2 | static randomFrom(anEnum: T): T[keyof T] { 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | // @ts-ignore 5 | const enumValues = Object.keys(anEnum).filter((key) => 6 | isNaN(Number(key)), 7 | ) as unknown as T[keyof T][]; 8 | const randomIndex = Math.floor(Math.random() * enumValues.length); 9 | 10 | return anEnum[enumValues[randomIndex] as keyof typeof anEnum]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/1-add_chatgpt/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | suggested_courses TEXT 12 | ); 13 | 14 | CREATE TABLE mooc__user_course_suggestions ( 15 | user_id UUID PRIMARY KEY, 16 | completed_courses TEXT, 17 | suggested_courses TEXT 18 | ); 19 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### GET a user 2 | GET http://localhost:3000/api/mooc/users/5353ef23-e982-4acc-928e-c7cdad92152a 3 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/06-chatgpt/2-tokens_calculation/src/app/favicon.ico -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/mooc/user_course_suggestions/domain/CourseSuggestionsGenerator.ts: -------------------------------------------------------------------------------- 1 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 2 | 3 | export interface CourseSuggestionsGenerator { 4 | generate(userCourseSuggestions: UserCourseSuggestions): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/mooc/user_course_suggestions/domain/UserCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../users/domain/UserId"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface UserCourseSuggestionsRepository { 5 | save(suggestions: UserCourseSuggestions): Promise; 6 | 7 | search(userId: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/mooc/users/domain/CoursesSuggestionLlm.ts: -------------------------------------------------------------------------------- 1 | export interface CoursesSuggestionLlm { 2 | predict(finishedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/tests/contexts/mooc/users/domain/DateMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | export class DateMother { 4 | static create(value?: string): Date { 5 | return new Date(value ?? faker.date.recent()); 6 | } 7 | 8 | static today(): Date { 9 | return new Date(); 10 | } 11 | 12 | static yesterday(): Date { 13 | const today = new Date(); 14 | const yesterday = new Date(today); 15 | yesterday.setDate(today.getDate() - 1); 16 | 17 | return yesterday; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/tests/contexts/shared/domain/EnumMother.ts: -------------------------------------------------------------------------------- 1 | export class EnumMother { 2 | static randomFrom(anEnum: T): T[keyof T] { 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | // @ts-ignore 5 | const enumValues = Object.keys(anEnum).filter((key) => 6 | isNaN(Number(key)), 7 | ) as unknown as T[keyof T][]; 8 | const randomIndex = Math.floor(Math.random() * enumValues.length); 9 | 10 | return anEnum[enumValues[randomIndex] as keyof typeof anEnum]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /06-chatgpt/2-tokens_calculation/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | suggested_courses TEXT 12 | ); 13 | 14 | CREATE TABLE mooc__user_course_suggestions ( 15 | user_id UUID PRIMARY KEY, 16 | completed_courses TEXT, 17 | suggested_courses TEXT 18 | ); 19 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### GET a user 2 | GET http://localhost:3000/api/mooc/users/5353ef23-e982-4acc-928e-c7cdad92152a 3 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/07-good_practices/2-implement_few_shot/src/app/favicon.ico -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/mooc/user_course_suggestions/domain/CourseSuggestionsGenerator.ts: -------------------------------------------------------------------------------- 1 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 2 | 3 | export interface CourseSuggestionsGenerator { 4 | generate(userCourseSuggestions: UserCourseSuggestions): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/mooc/user_course_suggestions/domain/UserCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../users/domain/UserId"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface UserCourseSuggestionsRepository { 5 | save(suggestions: UserCourseSuggestions): Promise; 6 | 7 | search(userId: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/mooc/users/domain/CoursesSuggestionLlm.ts: -------------------------------------------------------------------------------- 1 | export interface CoursesSuggestionLlm { 2 | predict(finishedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/tests/contexts/shared/domain/EnumMother.ts: -------------------------------------------------------------------------------- 1 | export class EnumMother { 2 | static randomFrom(anEnum: T): T[keyof T] { 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | // @ts-ignore 5 | const enumValues = Object.keys(anEnum).filter((key) => 6 | isNaN(Number(key)), 7 | ) as unknown as T[keyof T][]; 8 | const randomIndex = Math.floor(Math.random() * enumValues.length); 9 | 10 | return anEnum[enumValues[randomIndex] as keyof typeof anEnum]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/2-implement_few_shot/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | suggested_courses TEXT 12 | ); 13 | 14 | CREATE TABLE mooc__user_course_suggestions ( 15 | user_id UUID PRIMARY KEY, 16 | completed_courses TEXT, 17 | suggested_courses TEXT 18 | ); 19 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### GET a user 2 | GET http://localhost:3000/api/mooc/users/5353ef23-e982-4acc-928e-c7cdad92152a 3 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/07-good_practices/3-add_types/src/app/favicon.ico -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/mooc/user_course_suggestions/domain/CourseSuggestionsGenerator.ts: -------------------------------------------------------------------------------- 1 | import { CourseSuggestion } from "./CourseSuggestion"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface CourseSuggestionsGenerator { 5 | generate(userCourseSuggestions: UserCourseSuggestions): Promise; 6 | } 7 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/mooc/user_course_suggestions/domain/UserCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../users/domain/UserId"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface UserCourseSuggestionsRepository { 5 | save(suggestions: UserCourseSuggestions): Promise; 6 | 7 | search(userId: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/mooc/users/domain/CoursesSuggestionLlm.ts: -------------------------------------------------------------------------------- 1 | export interface CoursesSuggestionLlm { 2 | predict(finishedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/tests/contexts/mooc/users/domain/DateMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | export class DateMother { 4 | static create(value?: string): Date { 5 | return new Date(value ?? faker.date.recent()); 6 | } 7 | 8 | static today(): Date { 9 | return new Date(); 10 | } 11 | 12 | static yesterday(): Date { 13 | const today = new Date(); 14 | const yesterday = new Date(today); 15 | yesterday.setDate(today.getDate() - 1); 16 | 17 | return yesterday; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/tests/contexts/shared/domain/EnumMother.ts: -------------------------------------------------------------------------------- 1 | export class EnumMother { 2 | static randomFrom(anEnum: T): T[keyof T] { 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | // @ts-ignore 5 | const enumValues = Object.keys(anEnum).filter((key) => 6 | isNaN(Number(key)), 7 | ) as unknown as T[keyof T][]; 8 | const randomIndex = Math.floor(Math.random() * enumValues.length); 9 | 10 | return anEnum[enumValues[randomIndex] as keyof typeof anEnum]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /07-good_practices/3-add_types/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | suggested_courses TEXT 12 | ); 13 | 14 | CREATE TABLE mooc__user_course_suggestions ( 15 | user_id UUID PRIMARY KEY, 16 | completed_courses TEXT, 17 | suggested_courses TEXT 18 | ); 19 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### GET a user 2 | GET http://localhost:3000/api/mooc/users/5353ef23-e982-4acc-928e-c7cdad92152a 3 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/08-testing/1-debug_prompt/src/app/favicon.ico -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/mooc/user_course_suggestions/domain/CourseSuggestionsGenerator.ts: -------------------------------------------------------------------------------- 1 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 2 | 3 | export interface CourseSuggestionsGenerator { 4 | generate(userCourseSuggestions: UserCourseSuggestions): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/mooc/user_course_suggestions/domain/UserCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../users/domain/UserId"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface UserCourseSuggestionsRepository { 5 | save(suggestions: UserCourseSuggestions): Promise; 6 | 7 | search(userId: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/mooc/users/domain/CoursesSuggestionLlm.ts: -------------------------------------------------------------------------------- 1 | export interface CoursesSuggestionLlm { 2 | predict(finishedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/tests/contexts/mooc/users/domain/DateMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | export class DateMother { 4 | static create(value?: string): Date { 5 | return new Date(value ?? faker.date.recent()); 6 | } 7 | 8 | static today(): Date { 9 | return new Date(); 10 | } 11 | 12 | static yesterday(): Date { 13 | const today = new Date(); 14 | const yesterday = new Date(today); 15 | yesterday.setDate(today.getDate() - 1); 16 | 17 | return yesterday; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/tests/contexts/shared/domain/EnumMother.ts: -------------------------------------------------------------------------------- 1 | export class EnumMother { 2 | static randomFrom(anEnum: T): T[keyof T] { 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | // @ts-ignore 5 | const enumValues = Object.keys(anEnum).filter((key) => 6 | isNaN(Number(key)), 7 | ) as unknown as T[keyof T][]; 8 | const randomIndex = Math.floor(Math.random() * enumValues.length); 9 | 10 | return anEnum[enumValues[randomIndex] as keyof typeof anEnum]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/1-debug_prompt/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | suggested_courses TEXT 12 | ); 13 | 14 | CREATE TABLE mooc__user_course_suggestions ( 15 | user_id UUID PRIMARY KEY, 16 | completed_courses TEXT, 17 | suggested_courses TEXT 18 | ); 19 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### GET a user 2 | GET http://localhost:3000/api/mooc/users/5353ef23-e982-4acc-928e-c7cdad92152a 3 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/08-testing/2-integration_tests/src/app/favicon.ico -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/mooc/user_course_suggestions/domain/CourseSuggestionsGenerator.ts: -------------------------------------------------------------------------------- 1 | import { CourseSuggestion } from "./CourseSuggestion"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface CourseSuggestionsGenerator { 5 | generate(userCourseSuggestions: UserCourseSuggestions): Promise; 6 | } 7 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/mooc/user_course_suggestions/domain/UserCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../users/domain/UserId"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface UserCourseSuggestionsRepository { 5 | save(suggestions: UserCourseSuggestions): Promise; 6 | 7 | search(userId: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/mooc/users/domain/CoursesSuggestionLlm.ts: -------------------------------------------------------------------------------- 1 | export interface CoursesSuggestionLlm { 2 | predict(finishedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/tests/contexts/mooc/users/domain/DateMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | export class DateMother { 4 | static create(value?: string): Date { 5 | return new Date(value ?? faker.date.recent()); 6 | } 7 | 8 | static today(): Date { 9 | return new Date(); 10 | } 11 | 12 | static yesterday(): Date { 13 | const today = new Date(); 14 | const yesterday = new Date(today); 15 | yesterday.setDate(today.getDate() - 1); 16 | 17 | return yesterday; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/tests/contexts/shared/domain/EnumMother.ts: -------------------------------------------------------------------------------- 1 | export class EnumMother { 2 | static randomFrom(anEnum: T): T[keyof T] { 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | // @ts-ignore 5 | const enumValues = Object.keys(anEnum).filter((key) => 6 | isNaN(Number(key)), 7 | ) as unknown as T[keyof T][]; 8 | const randomIndex = Math.floor(Math.random() * enumValues.length); 9 | 10 | return anEnum[enumValues[randomIndex] as keyof typeof anEnum]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/2-integration_tests/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "eslint-config-codely/typescript"], 3 | "overrides": [ 4 | { 5 | "files": ["*.ts", "*.tsx"], 6 | "parserOptions": { 7 | "project": ["./tsconfig.json"] 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/databases/codely.sql: -------------------------------------------------------------------------------- 1 | /* ------------------------- 2 | mooc CONTEXT 3 | ---------------------------- */ 4 | 5 | CREATE TABLE mooc__users ( 6 | id UUID PRIMARY KEY, 7 | name VARCHAR(255), 8 | email VARCHAR(255), 9 | profile_picture VARCHAR(255), 10 | status VARCHAR(255), 11 | suggested_courses TEXT 12 | ); 13 | 14 | CREATE TABLE mooc__user_course_suggestions ( 15 | user_id UUID PRIMARY KEY, 16 | completed_courses TEXT, 17 | suggested_courses TEXT 18 | ); 19 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/etc/request/mooc/users.http: -------------------------------------------------------------------------------- 1 | ### GET a user 2 | GET http://localhost:3000/api/mooc/users/5353ef23-e982-4acc-928e-c7cdad92152a 3 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | }; 6 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodelyTV/add_ai_follwing_best_practices-course/95fd558ae5ec9d98064d492210b993ec19fdc74d/08-testing/3-llm_ci/src/app/favicon.ico -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/mooc/user_course_suggestions/domain/CourseSuggestionsGenerator.ts: -------------------------------------------------------------------------------- 1 | import { CourseSuggestion } from "./CourseSuggestion"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface CourseSuggestionsGenerator { 5 | generate(userCourseSuggestions: UserCourseSuggestions): Promise; 6 | } 7 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/mooc/user_course_suggestions/domain/UserCourseSuggestionsRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../users/domain/UserId"; 2 | import { UserCourseSuggestions } from "./UserCourseSuggestions"; 3 | 4 | export interface UserCourseSuggestionsRepository { 5 | save(suggestions: UserCourseSuggestions): Promise; 6 | 7 | search(userId: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/mooc/users/domain/CoursesSuggestionLlm.ts: -------------------------------------------------------------------------------- 1 | export interface CoursesSuggestionLlm { 2 | predict(finishedCourses: string[]): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/mooc/users/domain/UserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class UserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/mooc/users/domain/UserEmail.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserEmail extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/mooc/users/domain/UserId.ts: -------------------------------------------------------------------------------- 1 | import { Identifier } from "../../../shared/domain/Identifier"; 2 | 3 | export class UserId extends Identifier {} 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/mooc/users/domain/UserName.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserName extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/mooc/users/domain/UserProfilePicture.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "../../../shared/domain/StringValueObject"; 2 | 3 | export class UserProfilePicture extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/mooc/users/domain/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./User"; 2 | import { UserId } from "./UserId"; 3 | 4 | export interface UserRepository { 5 | save(user: User): Promise; 6 | 7 | search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/mooc/users/domain/UserStatus.ts: -------------------------------------------------------------------------------- 1 | export enum UserStatus { 2 | Active = "active", 3 | Archived = "archived", 4 | } 5 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/retention/users/domain/RetentionUserDoesNotExist.ts: -------------------------------------------------------------------------------- 1 | export class RetentionUserDoesNotExist extends Error { 2 | constructor(id: string) { 3 | super(`The retention user ${id} does not exist`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/retention/users/domain/RetentionUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { UserId } from "../../../mooc/users/domain/UserId"; 2 | import { RetentionUser } from "./RetentionUser"; 3 | 4 | export abstract class RetentionUserRepository { 5 | abstract save(user: RetentionUser): Promise; 6 | 7 | abstract search(id: UserId): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/AggregateRoot.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./event/DomainEvent"; 2 | 3 | export abstract class AggregateRoot { 4 | private domainEvents: DomainEvent[] = []; 5 | 6 | pullDomainEvents(): DomainEvent[] { 7 | const domainEvents = this.domainEvents; 8 | this.domainEvents = []; 9 | 10 | return domainEvents; 11 | } 12 | 13 | record(event: DomainEvent): void { 14 | this.domainEvents.push(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/Clock.ts: -------------------------------------------------------------------------------- 1 | export interface Clock { 2 | now(): Date; 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/EmailAddress.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export class EmailAddress extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/Identifier.ts: -------------------------------------------------------------------------------- 1 | import { StringValueObject } from "./StringValueObject"; 2 | 3 | export abstract class Identifier extends StringValueObject {} 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/NumberValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class NumberValueObject { 2 | constructor(public readonly value: number) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/StringValueObject.ts: -------------------------------------------------------------------------------- 1 | export abstract class StringValueObject { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/criteria/FilterField.ts: -------------------------------------------------------------------------------- 1 | export class FilterField { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/criteria/FilterValue.ts: -------------------------------------------------------------------------------- 1 | export class FilterValue { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/criteria/OrderBy.ts: -------------------------------------------------------------------------------- 1 | export class OrderBy { 2 | constructor(public readonly value: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/criteria/OrderType.ts: -------------------------------------------------------------------------------- 1 | export enum OrderTypes { 2 | ASC = "ASC", 3 | DESC = "DESC", 4 | NONE = "NONE", 5 | } 6 | 7 | export class OrderType { 8 | constructor(public readonly value: OrderTypes) {} 9 | 10 | isNone(): boolean { 11 | return this.value === OrderTypes.NONE; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/event/DomainEventClass.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export type DomainEventClass = { 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | new (...args: any[]): T; 6 | eventName: string; 7 | }; 8 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/event/DomainEventSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | import { DomainEventClass } from "./DomainEventClass"; 3 | 4 | export interface DomainEventSubscriber { 5 | on(domainEvent: T): Promise; 6 | 7 | subscribedTo(): DomainEventClass[]; 8 | 9 | name(): string; 10 | } 11 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/src/contexts/shared/domain/event/EventBus.ts: -------------------------------------------------------------------------------- 1 | import { DomainEvent } from "./DomainEvent"; 2 | 3 | export abstract class EventBus { 4 | abstract publish(events: DomainEvent[]): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/tests/contexts/mooc/users/domain/DateMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | export class DateMother { 4 | static create(value?: string): Date { 5 | return new Date(value ?? faker.date.recent()); 6 | } 7 | 8 | static today(): Date { 9 | return new Date(); 10 | } 11 | 12 | static yesterday(): Date { 13 | const today = new Date(); 14 | const yesterday = new Date(today); 15 | yesterday.setDate(today.getDate() - 1); 16 | 17 | return yesterday; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/tests/contexts/mooc/users/domain/UserEmailMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserEmail } from "../../../../../src/contexts/mooc/users/domain/UserEmail"; 4 | 5 | export class UserEmailMother { 6 | static create(value?: string): UserEmail { 7 | return new UserEmail(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/tests/contexts/mooc/users/domain/UserIdMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserId } from "../../../../../src/contexts/mooc/users/domain/UserId"; 4 | 5 | export class UserIdMother { 6 | static create(value?: string): UserId { 7 | return new UserId(value ?? faker.string.uuid()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/tests/contexts/mooc/users/domain/UserNameMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserName } from "../../../../../src/contexts/mooc/users/domain/UserName"; 4 | 5 | export class UserNameMother { 6 | static create(value?: string): UserName { 7 | return new UserName(value ?? faker.person.firstName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/tests/contexts/mooc/users/domain/UserProfilePictureMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { UserProfilePicture } from "../../../../../src/contexts/mooc/users/domain/UserProfilePicture"; 4 | 5 | export class UserProfilePictureMother { 6 | static create(value?: string): UserProfilePicture { 7 | return new UserProfilePicture(value ?? faker.image.url()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/tests/contexts/shared/domain/EmailAddressMother.ts: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | 3 | import { EmailAddress } from "../../../../src/contexts/shared/domain/EmailAddress"; 4 | 5 | export class EmailAddressMother { 6 | static create(value?: string): EmailAddress { 7 | return new EmailAddress(value ?? faker.internet.email()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/tests/contexts/shared/domain/EnumMother.ts: -------------------------------------------------------------------------------- 1 | export class EnumMother { 2 | static randomFrom(anEnum: T): T[keyof T] { 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | // @ts-ignore 5 | const enumValues = Object.keys(anEnum).filter((key) => 6 | isNaN(Number(key)), 7 | ) as unknown as T[keyof T][]; 8 | const randomIndex = Math.floor(Math.random() * enumValues.length); 9 | 10 | return anEnum[enumValues[randomIndex] as keyof typeof anEnum]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/tests/contexts/shared/domain/criteria/FiltersMother.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from "../../../../../src/contexts/shared/domain/criteria/Filter"; 2 | import { Filters } from "../../../../../src/contexts/shared/domain/criteria/Filters"; 3 | import { FilterMother } from "./FilterMother"; 4 | 5 | export class FiltersMother { 6 | static create(params: Filter[] = []): Filters { 7 | return new Filters(params.length !== 0 ? params : [FilterMother.create()]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /08-testing/3-llm_ci/tests/contexts/shared/infrastructure/MockClock.ts: -------------------------------------------------------------------------------- 1 | import { Clock } from "../../../../src/contexts/shared/domain/Clock"; 2 | 3 | export class MockClock implements Clock { 4 | private readonly mockNow = jest.fn(); 5 | 6 | now(): Date { 7 | expect(this.mockNow).toHaveBeenCalledWith(); 8 | 9 | return this.mockNow() as Date; 10 | } 11 | 12 | shouldGenerate(date: Date): void { 13 | this.mockNow(); 14 | this.mockNow.mockReturnValueOnce(date); 15 | } 16 | } 17 | --------------------------------------------------------------------------------