From 68c06e2508724db05e303cdf8b16d89e09f0b97c Mon Sep 17 00:00:00 2001 From: Alexander Kadyrov Date: Mon, 25 Nov 2024 22:22:55 +0400 Subject: [PATCH] Update Idea domain --- src/idea/adapters/IdeaRepositorySQLite.ts | 567 ++++++++---------- src/idea/app/commands/MakeReservation.ts | 22 +- .../commands/RequestSocialMediaCampaigns.ts | 5 +- src/idea/app/queries/GetIdea.ts | 69 +-- .../app/queries/GetSocialMediaCampaigns.ts | 7 +- src/idea/domain/Aggregate.ts | 123 +++- src/idea/domain/Repository.ts | 8 - src/idea/domain/events/IdeaArchived.ts | 4 +- src/idea/domain/events/IdeaCreated.ts | 4 +- .../events/SocialMediaCampaignsRequested.ts | 4 +- .../domain/events/TargetAudiencesEvaluated.ts | 14 + .../events/ValuePropositionEvaluated.ts | 4 +- .../CompetitorAnalysisEvaluationSubscriber.ts | 12 +- .../ContentIdeasEvaluationSubscriber.ts | 19 +- .../ElevatorPitchesEvaluationSubscriber.ts | 17 +- ...oogleTrendsKeywordsEvaluationSubscriber.ts | 17 +- .../MarketAnalysisEvaluationSubscriber.ts | 12 +- .../PotentialNamesEvaluationSubscriber.ts | 10 +- .../SWOTAnalysisEvaluationSubscriber.ts | 19 +- .../SocialMediaCampaignsSubscriber.ts | 19 +- .../TargetAudienceEvaluationSubscriber.ts | 15 +- .../ValuePropositionEvaluationSubscriber.ts | 12 +- src/idea/service/Service.ts | 39 +- 23 files changed, 467 insertions(+), 555 deletions(-) create mode 100644 src/idea/domain/events/TargetAudiencesEvaluated.ts diff --git a/src/idea/adapters/IdeaRepositorySQLite.ts b/src/idea/adapters/IdeaRepositorySQLite.ts index 4392c34..a37acb2 100644 --- a/src/idea/adapters/IdeaRepositorySQLite.ts +++ b/src/idea/adapters/IdeaRepositorySQLite.ts @@ -18,6 +18,10 @@ type UpdateFn = (idea: Idea) => Idea export class IdeaRepositorySQLite implements Repository { async addIdea(idea: Idea): Promise { + if (idea.getTargetAudiences().length === 0) { + throw new Error('Idea does not have target audiences') + } + await prisma.idea.create({ data: { id: idea.getId().getValue(), @@ -261,6 +265,10 @@ export class IdeaRepositorySQLite implements Repository { async getById(id: string): Promise { const ideaModel = await prisma.idea.findUnique({ where: { id }, + include: { + targetAudiences: true, + contents: true, + }, }) if (!ideaModel) { @@ -271,25 +279,10 @@ export class IdeaRepositorySQLite implements Repository { return null } - const idea = Idea.New( - ideaModel.id, - ideaModel.conceptId, - ideaModel.problem, - ideaModel.marketExistence - ) - - return idea - } - - async getTargetAudiencesByIdeaId(ideaId: string): Promise { - const targetAudienceModels = await prisma.targetAudience.findMany({ - where: { ideaId }, - }) - - return targetAudienceModels.map((audience) => { + const targetAudiences = ideaModel.targetAudiences.map((audience) => { const targetAudience = TargetAudience.New( audience.id, - ideaId, + ideaModel.id, audience.segment, audience.description, JSON.parse(audience.challenges) @@ -309,365 +302,305 @@ export class IdeaRepositorySQLite implements Repository { return targetAudience }) - } - - async getValuePropositionByIdeaId( - ideaId: string - ): Promise { - const valuePropositionModel = await prisma.ideaContent.findUnique({ - where: { - ideaId_key: { - ideaId: ideaId, - key: 'value_proposition', - }, - }, - }) - - if (!valuePropositionModel) { - return null - } - interface valueProposition { - mainBenefit: string - problemSolving: string - differentiation: string - } - - const data = JSON.parse(valuePropositionModel.value) as valueProposition + const idea = Idea.New( + ideaModel.id, + ideaModel.conceptId, + ideaModel.problem, + ideaModel.marketExistence, + targetAudiences + ) - return ValueProposition.New( - data.mainBenefit, - data.problemSolving, - data.differentiation + // Value proposition + const valuePropositionModel = ideaModel.contents.find( + (content) => content.key === 'value_proposition' ) - } - async getMarketAnalysisByIdeaId( - ideaId: string - ): Promise { - const marketAnalysisModel = await prisma.ideaContent.findUnique({ - where: { - ideaId_key: { - ideaId: ideaId, - key: 'market_analysis', - }, - }, - }) + if (valuePropositionModel) { + interface valueProposition { + mainBenefit: string + problemSolving: string + differentiation: string + } - if (!marketAnalysisModel) { - return null - } + const data = JSON.parse(valuePropositionModel.value) as valueProposition - interface marketAnalysis { - trends: string - userBehaviors: string - marketGaps: string - innovationOpportunities: string - strategicDirection: string + idea.setValueProposition( + ValueProposition.New( + data.mainBenefit, + data.problemSolving, + data.differentiation + ) + ) } - const data = JSON.parse(marketAnalysisModel.value) as marketAnalysis - - return MarketAnalysis.New( - data.trends, - data.userBehaviors, - data.marketGaps, - data.innovationOpportunities, - data.strategicDirection + // Market analysis + const marketAnalysisModel = ideaModel.contents.find( + (content) => content.key === 'market_analysis' ) - } - async getCompetitorAnalysisByIdeaId( - ideaId: string - ): Promise { - const competitorAnalysisModel = await prisma.ideaContent.findUnique({ - where: { - ideaId_key: { - ideaId: ideaId, - key: 'competitor_analysis', - }, - }, - }) - - if (!competitorAnalysisModel) { - return null - } - - interface competitor { - name: string - productName: string - url: string - coreFeatures: string[] - valueProposition: string - userAcquisition: string - strengths: string[] - weaknesses: string[] - differentiationOpportunity: string - } + if (marketAnalysisModel) { + interface marketAnalysis { + trends: string + userBehaviors: string + marketGaps: string + innovationOpportunities: string + strategicDirection: string + } - interface comparison { - strengths: string[] - weaknesses: string[] - } + const data = JSON.parse(marketAnalysisModel.value) as marketAnalysis - interface competitorAnalysis { - competitors: competitor[] - comparison: comparison - differentiationSuggestions: string[] + idea.setMarketAnalysis( + MarketAnalysis.New( + data.trends, + data.userBehaviors, + data.marketGaps, + data.innovationOpportunities, + data.strategicDirection + ) + ) } - const data = JSON.parse(competitorAnalysisModel.value) as competitorAnalysis - - return CompetitorAnalysis.New( - data.competitors, - data.comparison, - data.differentiationSuggestions + // Competitor analysis + const competitorAnalysisModel = ideaModel.contents.find( + (content) => content.key === 'competitor_analysis' ) - } - async getProductNamesByIdeaId(ideaId: string): Promise { - const productNamesModel = await prisma.ideaContent.findUnique({ - where: { - ideaId_key: { - ideaId: ideaId, - key: 'product_names', - }, - }, - }) - - if (!productNamesModel) { - return null - } + if (competitorAnalysisModel) { + interface competitor { + name: string + productName: string + url: string + coreFeatures: string[] + valueProposition: string + userAcquisition: string + strengths: string[] + weaknesses: string[] + differentiationOpportunity: string + } - interface productName { - productName: string - domains: string[] - why: string - tagline: string - targetAudienceInsight: string - similarNames: string[] - brandingPotential: string - } + interface comparison { + strengths: string[] + weaknesses: string[] + } - type productNames = productName[] + interface competitorAnalysis { + competitors: competitor[] + comparison: comparison + differentiationSuggestions: string[] + } - const data = JSON.parse(productNamesModel.value) as productNames + const data = JSON.parse( + competitorAnalysisModel.value + ) as competitorAnalysis - return data.map((product) => - ProductName.New( - product.productName, - product.domains, - product.why, - product.tagline, - product.targetAudienceInsight, - product.similarNames, - product.brandingPotential + idea.setCompetitorAnalysis( + CompetitorAnalysis.New( + data.competitors, + data.comparison, + data.differentiationSuggestions + ) ) - ) - } - - async getSWOTAnalysisByIdeaId(ideaId: string): Promise { - const swotAnalysisModel = await prisma.ideaContent.findUnique({ - where: { - ideaId_key: { - ideaId: ideaId, - key: 'swot_analysis', - }, - }, - }) - - if (!swotAnalysisModel) { - return null - } - - interface swotAnalysis { - strengths: string[] - weaknesses: string[] - opportunities: string[] - threats: string[] } - const data = JSON.parse(swotAnalysisModel.value) as swotAnalysis - - return SWOTAnalysis.New( - data.strengths, - data.weaknesses, - data.opportunities, - data.threats + // Product names + const productNamesModel = ideaModel.contents.find( + (content) => content.key === 'product_names' ) - } - async getElevatorPitchesByIdeaId( - ideaId: string - ): Promise { - const elevatorPitchesModel = await prisma.ideaContent.findUnique({ - where: { - ideaId_key: { - ideaId: ideaId, - key: 'elevator_pitches', - }, - }, - }) + if (productNamesModel) { + interface productName { + productName: string + domains: string[] + why: string + tagline: string + targetAudienceInsight: string + similarNames: string[] + brandingPotential: string + } - if (!elevatorPitchesModel) { - return null + type productNames = productName[] + + const data = JSON.parse(productNamesModel.value) as productNames + + data.forEach((product) => { + idea.addProductName( + ProductName.New( + product.productName, + product.domains, + product.why, + product.tagline, + product.targetAudienceInsight, + product.similarNames, + product.brandingPotential + ) + ) + }) } - interface elevatorPitch { - hook: string - problem: string - solution: string - valueProposition: string - cta: string - } + // SWOT analysis + const swotAnalysisModel = ideaModel.contents.find( + (content) => content.key === 'swot_analysis' + ) - type elevatorPitches = elevatorPitch[] + if (swotAnalysisModel) { + interface swotAnalysis { + strengths: string[] + weaknesses: string[] + opportunities: string[] + threats: string[] + } - const data = JSON.parse(elevatorPitchesModel.value) as elevatorPitches + const data = JSON.parse(swotAnalysisModel.value) as swotAnalysis - return data.map((pitch) => - ElevatorPitch.New( - pitch.hook, - pitch.problem, - pitch.solution, - pitch.valueProposition, - pitch.cta + idea.setSWOTAnalysis( + SWOTAnalysis.New( + data.strengths, + data.weaknesses, + data.opportunities, + data.threats + ) ) + } + + // Elevator pitches + const elevatorPitchesModel = ideaModel.contents.find( + (content) => content.key === 'elevator_pitches' ) - } - async getGoogleTrendsKeywordsByIdeaId( - ideaId: string - ): Promise { - const googleTrendsKeywordsModel = await prisma.ideaContent.findUnique({ - where: { - ideaId_key: { - ideaId: ideaId, - key: 'google_trends_keywords', - }, - }, - }) + if (elevatorPitchesModel) { + interface elevatorPitch { + hook: string + problem: string + solution: string + valueProposition: string + cta: string + } - if (!googleTrendsKeywordsModel) { - return null - } + type elevatorPitches = elevatorPitch[] - type keyword = { - keyword: string + const data = JSON.parse(elevatorPitchesModel.value) as elevatorPitches + + data.forEach((pitch) => { + idea.addElevatorPitch( + ElevatorPitch.New( + pitch.hook, + pitch.problem, + pitch.solution, + pitch.valueProposition, + pitch.cta + ) + ) + }) } - type keywords = keyword[] + // Google Trends keywords + const googleTrendsKeywordsModel = ideaModel.contents.find( + (content) => content.key === 'google_trends_keywords' + ) - const data = JSON.parse(googleTrendsKeywordsModel.value) as keywords + if (googleTrendsKeywordsModel) { + type keyword = { + keyword: string + } - return data.map((value) => GoogleTrendsKeyword.New(value.keyword)) - } + type keywords = keyword[] - async getContentIdeasForMarketingByIdeaId( - ideaId: string - ): Promise { - const contentIdeasModel = await prisma.ideaContent.findUnique({ - where: { - ideaId_key: { - ideaId: ideaId, - key: 'content_ideas_for_marketing', - }, - }, - }) + const data = JSON.parse(googleTrendsKeywordsModel.value) as keywords - if (!contentIdeasModel) { - return null + data.forEach((value) => { + idea.addGoogleTrendsKeyword(GoogleTrendsKeyword.New(value.keyword)) + }) } - interface contentIdea { - strategy: { - name: string + // Content ideas for marketing + const contentIdeasModel = ideaModel.contents.find( + (content) => content.key === 'content_ideas_for_marketing' + ) + + if (contentIdeasModel) { + interface contentIdea { + strategy: { + name: string + } + platforms: string[] + ideas: string[] + benefits: string[] } - platforms: string[] - ideas: string[] - benefits: string[] - } - interface contentIdeas { - contentIdeas: contentIdea[] - } + interface contentIdeas { + contentIdeas: contentIdea[] + } - const data = JSON.parse(contentIdeasModel.value) as contentIdeas + const data = JSON.parse(contentIdeasModel.value) as contentIdeas - const contentIdeasForMarketing = ContentIdeasForMarketing.New() + const contentIdeasForMarketing = ContentIdeasForMarketing.New() - data.contentIdeas.forEach((strategy) => { - contentIdeasForMarketing.addContentIdea( - ContentIdea.New( - Strategy.New(strategy.strategy.name), - strategy.platforms, - strategy.ideas, - strategy.benefits + data.contentIdeas.forEach((strategy) => { + contentIdeasForMarketing.addContentIdea( + ContentIdea.New( + Strategy.New(strategy.strategy.name), + strategy.platforms, + strategy.ideas, + strategy.benefits + ) ) - ) - }) + }) - return contentIdeasForMarketing - } + idea.setContentIdeasForMarketing(contentIdeasForMarketing) + } - async getSocialMediaCampaignsByIdeaId( - ideaId: string - ): Promise { - const socialMediaCampaignsModel = await prisma.ideaContent.findUnique({ - where: { - ideaId_key: { - ideaId: ideaId, - key: 'social_media_campaigns', - }, - }, - }) + // Social media campaigns + const socialMediaCampaignsModel = ideaModel.contents.find( + (content) => content.key === 'social_media_campaigns' + ) - if (!socialMediaCampaignsModel) { - return null - } + if (socialMediaCampaignsModel) { + interface campaigns { + shortFormContents: Array<{ + header: string + platform: string + content: string + tips: string[] + imagePrompt: string + }> + longFormContents: Array<{ + header: string + platform: string + title: string + content: string + tips: string[] + imagePrompt: string + }> + videoContents: Array<{ + header: string + platform: string + title: string + script: string[] + tips: string[] + imagePrompt: string + }> + } - interface campaigns { - shortFormContents: Array<{ - header: string - platform: string - content: string - tips: string[] - imagePrompt: string - }> - longFormContents: Array<{ - header: string - platform: string - title: string - content: string - tips: string[] - imagePrompt: string - }> - videoContents: Array<{ - header: string - platform: string - title: string - script: string[] - tips: string[] - imagePrompt: string - }> - } + const data = JSON.parse(socialMediaCampaignsModel.value) as campaigns - const data = JSON.parse(socialMediaCampaignsModel.value) as campaigns + const socialMediaCampaigns = SocialMediaCampaigns.New() - const contentIdeasForMarketing = SocialMediaCampaigns.New() + data.shortFormContents.forEach((content) => { + socialMediaCampaigns.addShortFormContent(content) + }) - data.shortFormContents.forEach((content) => { - contentIdeasForMarketing.addShortFormContent(content) - }) + data.longFormContents.forEach((content) => { + socialMediaCampaigns.addLongFormContent(content) + }) - data.longFormContents.forEach((content) => { - contentIdeasForMarketing.addLongFormContent(content) - }) + data.videoContents.forEach((content) => { + socialMediaCampaigns.addVideoContent(content) + }) - data.videoContents.forEach((content) => { - contentIdeasForMarketing.addVideoContent(content) - }) + idea.setSocialMediaCampaigns(socialMediaCampaigns) + } - return contentIdeasForMarketing + return idea } } diff --git a/src/idea/app/commands/MakeReservation.ts b/src/idea/app/commands/MakeReservation.ts index 8a84fdc..4349fc2 100644 --- a/src/idea/app/commands/MakeReservation.ts +++ b/src/idea/app/commands/MakeReservation.ts @@ -59,15 +59,8 @@ export class MakeReservationHandler { ) } - const idea = Idea.New( - command.ideaId, - command.conceptId, - concept.content.problem, - concept.content.marketExistence - ) - - concept.content.targetAudience.forEach((targetAudience) => { - idea.addTargetAudience( + const targetAudiences = concept.content.targetAudience.map( + (targetAudience) => TargetAudience.New( randomUUID(), command.ideaId, @@ -75,8 +68,15 @@ export class MakeReservationHandler { targetAudience.description, targetAudience.challenges ) - ) - }) + ) + + const idea = Idea.New( + command.ideaId, + command.conceptId, + concept.content.problem, + concept.content.marketExistence, + targetAudiences + ) await this.repository.addIdea(idea) diff --git a/src/idea/app/commands/RequestSocialMediaCampaigns.ts b/src/idea/app/commands/RequestSocialMediaCampaigns.ts index ab70155..0afc474 100644 --- a/src/idea/app/commands/RequestSocialMediaCampaigns.ts +++ b/src/idea/app/commands/RequestSocialMediaCampaigns.ts @@ -25,10 +25,7 @@ export class RequestSocialMediaCampaignsHandler { throw new Error(`Idea ${command.ideaId} does not exist`) } - const socialMediaCampaigns = - await this.repository.getSocialMediaCampaignsByIdeaId(command.ideaId) - - if (socialMediaCampaigns) { + if (idea.getSocialMediaCampaigns()) { return } diff --git a/src/idea/app/queries/GetIdea.ts b/src/idea/app/queries/GetIdea.ts index 4674fdb..2e6cc7e 100644 --- a/src/idea/app/queries/GetIdea.ts +++ b/src/idea/app/queries/GetIdea.ts @@ -1,15 +1,6 @@ import * as Sentry from '@sentry/nextjs' import { NotFoundError } from '@/common/errors/NotFoundError' import { Idea } from '@/idea/domain/Aggregate' -import { CompetitorAnalysis } from '@/idea/domain/CompetitorAnalysis' -import { ContentIdeasForMarketing } from '@/idea/domain/ContentIdeasForMarketing' -import { ElevatorPitch } from '@/idea/domain/ElevatorPitch' -import { GoogleTrendsKeyword } from '@/idea/domain/GoogleTrendsKeyword' -import { MarketAnalysis } from '@/idea/domain/MarketAnalysis' -import { ProductName } from '@/idea/domain/ProductName' -import { SWOTAnalysis } from '@/idea/domain/SWOTAnalysis' -import { TargetAudience } from '@/idea/domain/TargetAudience' -import { ValueProposition } from '@/idea/domain/ValueProposition' type Query = { id: string @@ -92,21 +83,6 @@ interface ContentIdeaDTO { interface ReadModel { getById(id: string): Promise - getTargetAudiencesByIdeaId(ideaId: string): Promise - getValuePropositionByIdeaId(ideaId: string): Promise - getMarketAnalysisByIdeaId(ideaId: string): Promise - getCompetitorAnalysisByIdeaId( - ideaId: string - ): Promise - getProductNamesByIdeaId(ideaId: string): Promise - getSWOTAnalysisByIdeaId(ideaId: string): Promise - getElevatorPitchesByIdeaId(ideaId: string): Promise - getGoogleTrendsKeywordsByIdeaId( - ideaId: string - ): Promise - getContentIdeasForMarketingByIdeaId( - ideaId: string - ): Promise } export class GetIdeaHandler { @@ -124,38 +100,15 @@ export class GetIdeaHandler { throw new NotFoundError(`Idea ${query.id} does not exist`) } - const targetAudiences = await this.readModel.getTargetAudiencesByIdeaId( - query.id - ) - - const valueProposition = await this.readModel.getValuePropositionByIdeaId( - query.id - ) - - const marketAnalysis = await this.readModel.getMarketAnalysisByIdeaId( - query.id - ) - - const competitorAnalysis = - await this.readModel.getCompetitorAnalysisByIdeaId(query.id) - - const productNames = await this.readModel.getProductNamesByIdeaId( - query.id - ) - - const swotAnalysis = await this.readModel.getSWOTAnalysisByIdeaId( - query.id - ) - - const elevatorPitches = await this.readModel.getElevatorPitchesByIdeaId( - query.id - ) - - const googleTrendsKeywords = - await this.readModel.getGoogleTrendsKeywordsByIdeaId(query.id) - - const contentIdeas = - await this.readModel.getContentIdeasForMarketingByIdeaId(query.id) + const valueProposition = idea.getValueProposition() + const targetAudiences = idea.getTargetAudiences() + const marketAnalysis = idea.getMarketAnalysis() + const competitorAnalysis = idea.getCompetitorAnalysis() + const productNames = idea.getProductNames() + const swotAnalysis = idea.getSWOTAnalysis() + const elevatorPitches = idea.getElevatorPitches() + const googleTrendsKeywords = idea.getGoogleTrendsKeywords() + const contentIdeasForMarketing = idea.getContentIdeasForMarketing() return { id: idea.getId().getValue(), @@ -225,8 +178,8 @@ export class GetIdeaHandler { googleTrendsKeywords: googleTrendsKeywords ? googleTrendsKeywords.map((keyword) => keyword.getKeyword()) : null, - contentIdeasForMarketing: contentIdeas - ? contentIdeas.getContentIdeas().reduce( + contentIdeasForMarketing: contentIdeasForMarketing + ? contentIdeasForMarketing.getContentIdeas().reduce( (acc, contentIdea) => { const section = contentIdea.getSection().getName() diff --git a/src/idea/app/queries/GetSocialMediaCampaigns.ts b/src/idea/app/queries/GetSocialMediaCampaigns.ts index 42f157d..2d245de 100644 --- a/src/idea/app/queries/GetSocialMediaCampaigns.ts +++ b/src/idea/app/queries/GetSocialMediaCampaigns.ts @@ -1,7 +1,6 @@ import * as Sentry from '@sentry/nextjs' import { NotFoundError } from '@/common/errors/NotFoundError' import { Idea } from '@/idea/domain/Aggregate' -import { SocialMediaCampaigns } from '@/idea/domain/SocialMediaCampaigns' type Query = { id: string @@ -44,9 +43,6 @@ interface DTO { interface ReadModel { getById(id: string): Promise - getSocialMediaCampaignsByIdeaId( - ideaId: string - ): Promise } export class GetSocialMediaCampaignsHandler { @@ -64,8 +60,7 @@ export class GetSocialMediaCampaignsHandler { throw new NotFoundError(`Idea ${query.id} does not exist`) } - const socialMediaCampaigns = - await this.readModel.getSocialMediaCampaignsByIdeaId(query.id) + const socialMediaCampaigns = idea.getSocialMediaCampaigns() return { id: idea.getId().getValue(), diff --git a/src/idea/domain/Aggregate.ts b/src/idea/domain/Aggregate.ts index 24b604a..28d8421 100644 --- a/src/idea/domain/Aggregate.ts +++ b/src/idea/domain/Aggregate.ts @@ -16,8 +16,8 @@ export class Idea { private readonly conceptId: Identity private readonly problem: Problem private readonly marketExistence: string + private readonly targetAudiences: TargetAudience[] = [] - private targetAudiences: TargetAudience[] = [] private valueProposition: ValueProposition | null = null private marketAnalysis: MarketAnalysis | null = null private competitorAnalysis: CompetitorAnalysis | null = null @@ -34,42 +34,91 @@ export class Idea { id: Identity, conceptId: Identity, problem: Problem, - marketExistence: string + marketExistence: string, + targetAudiences: TargetAudience[] ) { + if (targetAudiences.length === 0) { + throw new Error('Target audiences cannot be empty') + } + this.id = id this.conceptId = conceptId this.problem = problem this.marketExistence = marketExistence + this.targetAudiences = targetAudiences } static New( id: string, conceptId: string, problem: string, - marketExistence: string + marketExistence: string, + targetAudiences: TargetAudience[] ): Idea { return new Idea( Identity.New(id), Identity.New(conceptId), Problem.New(problem), - marketExistence + marketExistence, + targetAudiences ) } - public addTargetAudience(targetAudience: TargetAudience): void { - // TODO: If targetAudience already exists by ID - replace it - this.targetAudiences.push(targetAudience) + public updateTargetAudience(updatedAudience: TargetAudience): void { + const index = this.targetAudiences.findIndex( + (audience) => + audience.getId().getValue() === updatedAudience.getId().getValue() + ) + + if (index === -1) { + throw new Error( + `TargetAudience with ID ${updatedAudience.getId().getValue()} does not exist` + ) + } + + const why = updatedAudience.getWhy() + if (why === null) { + throw new Error('Why has not been set for this TargetAudience.') + } + + const painPoints = updatedAudience.getPainPoints() + if (painPoints === null) { + throw new Error('PainPoints have not been set for this TargetAudience.') + } + + const targetingStrategy = updatedAudience.getTargetingStrategy() + if (targetingStrategy === null) { + throw new Error( + 'TargetingStrategy has not been set for this TargetAudience.' + ) + } + + this.targetAudiences[index].setWhy(why) + this.targetAudiences[index].setPainPoints(painPoints) + this.targetAudiences[index].setTargetingStrategy(targetingStrategy) } - public addValueProposition(valueProposition: ValueProposition): void { + public setValueProposition(valueProposition: ValueProposition): void { + if (this.valueProposition !== null) { + throw new Error('ValueProposition already set') + } + this.valueProposition = valueProposition } - public addMarketAnalysis(marketAnalysis: MarketAnalysis): void { + public setMarketAnalysis(marketAnalysis: MarketAnalysis): void { + if (this.marketAnalysis !== null) { + throw new Error('MarketAnalysis already set') + } + this.marketAnalysis = marketAnalysis } - public addCompetitorAnalysis(competitorAnalysis: CompetitorAnalysis): void { + public setCompetitorAnalysis(competitorAnalysis: CompetitorAnalysis): void { + if (this.competitorAnalysis !== null) { + throw new Error('CompetitorAnalysis already set') + } + this.competitorAnalysis = competitorAnalysis } @@ -78,10 +127,22 @@ export class Idea { this.productNames = [] } + const exists = this.productNames.some( + (ta) => ta.getProductName() === productName.getProductName() + ) + + if (exists) { + throw new Error('ProductName already exists') + } + this.productNames.push(productName) } - public addSWOTAnalysis(swotAnalysis: SWOTAnalysis): void { + public setSWOTAnalysis(swotAnalysis: SWOTAnalysis): void { + if (this.swotAnalysis !== null) { + throw new Error('SWOTAnalysis already set') + } + this.swotAnalysis = swotAnalysis } @@ -90,6 +151,14 @@ export class Idea { this.elevatorPitches = [] } + const exists = this.elevatorPitches.some( + (ta) => ta.getHook() === elevatorPitch.getHook() + ) + + if (exists) { + throw new Error('ElevatorPitch already exists') + } + this.elevatorPitches.push(elevatorPitch) } @@ -98,24 +167,40 @@ export class Idea { this.googleTrendsKeywords = [] } + const exists = this.googleTrendsKeywords.some( + (ta) => ta.getKeyword() === keyword.getKeyword() + ) + + if (exists) { + throw new Error('GoogleTrendsKeyword already exists') + } + this.googleTrendsKeywords.push(keyword) } - public addContentIdeasForMarketing( + public setContentIdeasForMarketing( contentIdeas: ContentIdeasForMarketing ): void { + if (this.contentIdeasForMarketing !== null) { + throw new Error('ContentIdeasForMarketing already set') + } + this.contentIdeasForMarketing = contentIdeas } - public addSocialMediaCampaigns( + public setSocialMediaCampaigns( socialMediaCampaigns: SocialMediaCampaigns ): void { + if (this.socialMediaCampaigns !== null) { + throw new Error('SocialMediaCampaigns already set') + } + this.socialMediaCampaigns = socialMediaCampaigns } public finalizeMigration(): void { if (this.migrated) { - throw new Error('Idea was migrated') + throw new Error('Idea was already migrated') } this.migrated = true @@ -123,7 +208,7 @@ export class Idea { public archive(): void { if (this.archived) { - throw new Error('Idea was archived') + throw new Error('Idea was already archived') } this.archived = true @@ -145,7 +230,7 @@ export class Idea { return this.marketExistence } - public getTargetAudiences(): TargetAudience[] { + public getTargetAudiences(): ReadonlyArray { return this.targetAudiences } @@ -161,7 +246,7 @@ export class Idea { return this.competitorAnalysis } - public getProductNames(): ProductName[] | null { + public getProductNames(): ReadonlyArray | null { return this.productNames } @@ -169,11 +254,11 @@ export class Idea { return this.swotAnalysis } - public getElevatorPitches(): ElevatorPitch[] | null { + public getElevatorPitches(): ReadonlyArray | null { return this.elevatorPitches } - public getGoogleTrendsKeywords(): GoogleTrendsKeyword[] | null { + public getGoogleTrendsKeywords(): ReadonlyArray | null { return this.googleTrendsKeywords } diff --git a/src/idea/domain/Repository.ts b/src/idea/domain/Repository.ts index e06af09..3ed042c 100644 --- a/src/idea/domain/Repository.ts +++ b/src/idea/domain/Repository.ts @@ -1,15 +1,7 @@ import { Idea } from '@/idea/domain/Aggregate' -import { SocialMediaCampaigns } from '@/idea/domain/SocialMediaCampaigns' -import { TargetAudience } from '@/idea/domain/TargetAudience' -import { ValueProposition } from '@/idea/domain/ValueProposition' export interface Repository { addIdea(idea: Idea): Promise updateIdea(id: string, updateFn: (idea: Idea) => Idea): Promise getById(id: string): Promise - getTargetAudiencesByIdeaId(ideaId: string): Promise - getValuePropositionByIdeaId(ideaId: string): Promise - getSocialMediaCampaignsByIdeaId( - ideaId: string - ): Promise } diff --git a/src/idea/domain/events/IdeaArchived.ts b/src/idea/domain/events/IdeaArchived.ts index 33afc59..ed71520 100644 --- a/src/idea/domain/events/IdeaArchived.ts +++ b/src/idea/domain/events/IdeaArchived.ts @@ -1,7 +1,9 @@ import { Event } from '@/idea/events/Event' export class IdeaArchived implements Event { - public readonly type = 'IdeaArchived' + static eventName = 'IdeaArchived' + + public readonly type = IdeaArchived.eventName public readonly payload: { id: string } diff --git a/src/idea/domain/events/IdeaCreated.ts b/src/idea/domain/events/IdeaCreated.ts index 1c433a8..8ea28ac 100644 --- a/src/idea/domain/events/IdeaCreated.ts +++ b/src/idea/domain/events/IdeaCreated.ts @@ -1,7 +1,9 @@ import { Event } from '@/idea/events/Event' export class IdeaCreated implements Event { - public readonly type = 'IdeaCreated' + static eventName = 'IdeaCreated' + + public readonly type = IdeaCreated.eventName public readonly payload: { id: string } diff --git a/src/idea/domain/events/SocialMediaCampaignsRequested.ts b/src/idea/domain/events/SocialMediaCampaignsRequested.ts index 4916377..32d2d4a 100644 --- a/src/idea/domain/events/SocialMediaCampaignsRequested.ts +++ b/src/idea/domain/events/SocialMediaCampaignsRequested.ts @@ -1,7 +1,9 @@ import { Event } from '@/idea/events/Event' export class SocialMediaCampaignsRequested implements Event { - public readonly type = 'SocialMediaCampaignsRequested' + static eventName = 'SocialMediaCampaignsRequested' + + public readonly type = SocialMediaCampaignsRequested.eventName public readonly payload: { id: string } diff --git a/src/idea/domain/events/TargetAudiencesEvaluated.ts b/src/idea/domain/events/TargetAudiencesEvaluated.ts new file mode 100644 index 0000000..a369e73 --- /dev/null +++ b/src/idea/domain/events/TargetAudiencesEvaluated.ts @@ -0,0 +1,14 @@ +import { Event } from '@/idea/events/Event' + +export class TargetAudiencesEvaluated implements Event { + static eventName = 'TargetAudiencesEvaluated' + + public readonly type = TargetAudiencesEvaluated.eventName + public readonly payload: { + id: string + } + + constructor(id: string) { + this.payload = { id } + } +} diff --git a/src/idea/domain/events/ValuePropositionEvaluated.ts b/src/idea/domain/events/ValuePropositionEvaluated.ts index 4b93808..e0395f5 100644 --- a/src/idea/domain/events/ValuePropositionEvaluated.ts +++ b/src/idea/domain/events/ValuePropositionEvaluated.ts @@ -1,7 +1,9 @@ import { Event } from '@/idea/events/Event' export class ValuePropositionEvaluated implements Event { - public readonly type = 'ValuePropositionEvaluated' + static eventName = 'ValuePropositionEvaluated' + + public readonly type = ValuePropositionEvaluated.eventName public readonly payload: { id: string } diff --git a/src/idea/events/subscribers/CompetitorAnalysisEvaluationSubscriber.ts b/src/idea/events/subscribers/CompetitorAnalysisEvaluationSubscriber.ts index 69775d4..b4d0d6c 100644 --- a/src/idea/events/subscribers/CompetitorAnalysisEvaluationSubscriber.ts +++ b/src/idea/events/subscribers/CompetitorAnalysisEvaluationSubscriber.ts @@ -2,7 +2,7 @@ import * as Sentry from '@sentry/nextjs' import { Idea } from '@/idea/domain/Aggregate' import { CompetitorAnalysis } from '@/idea/domain/CompetitorAnalysis' import { Repository } from '@/idea/domain/Repository' -import { IdeaCreated } from '@/idea/domain/events/IdeaCreated' +import { TargetAudiencesEvaluated } from '@/idea/domain/events/TargetAudiencesEvaluated' import { EventHandler } from '@/idea/events/EventHandler' interface Competitor { @@ -55,7 +55,7 @@ export class CompetitorAnalysisEvaluationSubscriber implements EventHandler { return CompetitorAnalysisEvaluationSubscriber.className } - async handle(event: IdeaCreated): Promise { + async handle(event: TargetAudiencesEvaluated): Promise { Sentry.setTag('component', 'BackgroundJob') Sentry.setTag('job_type', this.getName()) Sentry.setTag('event_type', event.type) @@ -70,11 +70,7 @@ export class CompetitorAnalysisEvaluationSubscriber implements EventHandler { throw new Error(`Unable to get idea by ID: ${event.payload.id}`) } - const targetAudiences = await this.repository.getTargetAudiencesByIdeaId( - idea.getId().getValue() - ) - - const audiences = targetAudiences.map((targetAudience) => ({ + const audiences = idea.getTargetAudiences().map((targetAudience) => ({ segment: targetAudience.getSegment(), description: targetAudience.getDescription(), challenges: targetAudience.getChallenges(), @@ -88,7 +84,7 @@ export class CompetitorAnalysisEvaluationSubscriber implements EventHandler { ) await this.repository.updateIdea(event.payload.id, (idea): Idea => { - idea.addCompetitorAnalysis( + idea.setCompetitorAnalysis( CompetitorAnalysis.New( evaluation.competitors, evaluation.comparison, diff --git a/src/idea/events/subscribers/ContentIdeasEvaluationSubscriber.ts b/src/idea/events/subscribers/ContentIdeasEvaluationSubscriber.ts index 7525c40..8745773 100644 --- a/src/idea/events/subscribers/ContentIdeasEvaluationSubscriber.ts +++ b/src/idea/events/subscribers/ContentIdeasEvaluationSubscriber.ts @@ -74,26 +74,13 @@ export class ContentIdeasEvaluationSubscriber implements EventHandler { throw new Error(`Unable to get idea by ID: ${event.payload.id}`) } - const targetAudiences = await this.repository.getTargetAudiencesByIdeaId( - idea.getId().getValue() - ) - - if (targetAudiences.length === 0) { - throw new Error( - `Idea ${event.payload.id} does not have target audiences` - ) - } - - const audiences = targetAudiences.map((targetAudience) => ({ + const audiences = idea.getTargetAudiences().map((targetAudience) => ({ segment: targetAudience.getSegment(), description: targetAudience.getDescription(), challenges: targetAudience.getChallenges(), })) - const valueProposition = - await this.repository.getValuePropositionByIdeaId( - idea.getId().getValue() - ) + const valueProposition = idea.getValueProposition() if (!valueProposition) { throw new Error( @@ -125,7 +112,7 @@ export class ContentIdeasEvaluationSubscriber implements EventHandler { }) await this.repository.updateIdea(event.payload.id, (idea): Idea => { - idea.addContentIdeasForMarketing(contentIdeasForMarketing) + idea.setContentIdeasForMarketing(contentIdeasForMarketing) return idea }) diff --git a/src/idea/events/subscribers/ElevatorPitchesEvaluationSubscriber.ts b/src/idea/events/subscribers/ElevatorPitchesEvaluationSubscriber.ts index efba174..618edf0 100644 --- a/src/idea/events/subscribers/ElevatorPitchesEvaluationSubscriber.ts +++ b/src/idea/events/subscribers/ElevatorPitchesEvaluationSubscriber.ts @@ -63,26 +63,13 @@ export class ElevatorPitchesEvaluationSubscriber implements EventHandler { throw new Error(`Unable to get idea by ID: ${event.payload.id}`) } - const targetAudiences = await this.repository.getTargetAudiencesByIdeaId( - idea.getId().getValue() - ) - - if (targetAudiences.length === 0) { - throw new Error( - `Idea ${event.payload.id} does not have target audiences` - ) - } - - const audiences = targetAudiences.map((targetAudience) => ({ + const audiences = idea.getTargetAudiences().map((targetAudience) => ({ segment: targetAudience.getSegment(), description: targetAudience.getDescription(), challenges: targetAudience.getChallenges(), })) - const valueProposition = - await this.repository.getValuePropositionByIdeaId( - idea.getId().getValue() - ) + const valueProposition = idea.getValueProposition() if (!valueProposition) { throw new Error( diff --git a/src/idea/events/subscribers/GoogleTrendsKeywordsEvaluationSubscriber.ts b/src/idea/events/subscribers/GoogleTrendsKeywordsEvaluationSubscriber.ts index 403c4b6..ce06327 100644 --- a/src/idea/events/subscribers/GoogleTrendsKeywordsEvaluationSubscriber.ts +++ b/src/idea/events/subscribers/GoogleTrendsKeywordsEvaluationSubscriber.ts @@ -57,26 +57,13 @@ export class GoogleTrendsKeywordsEvaluationSubscriber implements EventHandler { throw new Error(`Unable to get idea by ID: ${event.payload.id}`) } - const targetAudiences = await this.repository.getTargetAudiencesByIdeaId( - idea.getId().getValue() - ) - - if (targetAudiences.length === 0) { - throw new Error( - `Idea ${event.payload.id} does not have target audiences` - ) - } - - const audiences = targetAudiences.map((targetAudience) => ({ + const audiences = idea.getTargetAudiences().map((targetAudience) => ({ segment: targetAudience.getSegment(), description: targetAudience.getDescription(), challenges: targetAudience.getChallenges(), })) - const valueProposition = - await this.repository.getValuePropositionByIdeaId( - idea.getId().getValue() - ) + const valueProposition = idea.getValueProposition() if (!valueProposition) { throw new Error( diff --git a/src/idea/events/subscribers/MarketAnalysisEvaluationSubscriber.ts b/src/idea/events/subscribers/MarketAnalysisEvaluationSubscriber.ts index 332bf9a..1a1a426 100644 --- a/src/idea/events/subscribers/MarketAnalysisEvaluationSubscriber.ts +++ b/src/idea/events/subscribers/MarketAnalysisEvaluationSubscriber.ts @@ -2,7 +2,7 @@ import * as Sentry from '@sentry/nextjs' import { Idea } from '@/idea/domain/Aggregate' import { MarketAnalysis } from '@/idea/domain/MarketAnalysis' import { Repository } from '@/idea/domain/Repository' -import { IdeaCreated } from '@/idea/domain/events/IdeaCreated' +import { TargetAudiencesEvaluated } from '@/idea/domain/events/TargetAudiencesEvaluated' import { EventHandler } from '@/idea/events/EventHandler' type Evaluation = { @@ -40,7 +40,7 @@ export class MarketAnalysisEvaluationSubscriber implements EventHandler { return MarketAnalysisEvaluationSubscriber.className } - async handle(event: IdeaCreated): Promise { + async handle(event: TargetAudiencesEvaluated): Promise { Sentry.setTag('component', 'BackgroundJob') Sentry.setTag('job_type', this.getName()) Sentry.setTag('event_type', event.type) @@ -55,11 +55,7 @@ export class MarketAnalysisEvaluationSubscriber implements EventHandler { throw new Error(`Unable to get idea by ID: ${event.payload.id}`) } - const targetAudiences = await this.repository.getTargetAudiencesByIdeaId( - idea.getId().getValue() - ) - - const audiences = targetAudiences.map((targetAudience) => ({ + const audiences = idea.getTargetAudiences().map((targetAudience) => ({ segment: targetAudience.getSegment(), description: targetAudience.getDescription(), challenges: targetAudience.getChallenges(), @@ -73,7 +69,7 @@ export class MarketAnalysisEvaluationSubscriber implements EventHandler { ) await this.repository.updateIdea(event.payload.id, (idea): Idea => { - idea.addMarketAnalysis( + idea.setMarketAnalysis( MarketAnalysis.New( evaluation.trends, evaluation.userBehaviors, diff --git a/src/idea/events/subscribers/PotentialNamesEvaluationSubscriber.ts b/src/idea/events/subscribers/PotentialNamesEvaluationSubscriber.ts index a9adc34..cdb71fb 100644 --- a/src/idea/events/subscribers/PotentialNamesEvaluationSubscriber.ts +++ b/src/idea/events/subscribers/PotentialNamesEvaluationSubscriber.ts @@ -2,7 +2,7 @@ import * as Sentry from '@sentry/nextjs' import { Idea } from '@/idea/domain/Aggregate' import { ProductName } from '@/idea/domain/ProductName' import { Repository } from '@/idea/domain/Repository' -import { IdeaCreated } from '@/idea/domain/events/IdeaCreated' +import { TargetAudiencesEvaluated } from '@/idea/domain/events/TargetAudiencesEvaluated' import { EventHandler } from '@/idea/events/EventHandler' interface PotentialName { @@ -44,7 +44,7 @@ export class PotentialNamesEvaluationSubscriber implements EventHandler { return PotentialNamesEvaluationSubscriber.className } - async handle(event: IdeaCreated): Promise { + async handle(event: TargetAudiencesEvaluated): Promise { Sentry.setTag('component', 'BackgroundJob') Sentry.setTag('job_type', this.getName()) Sentry.setTag('event_type', event.type) @@ -59,11 +59,7 @@ export class PotentialNamesEvaluationSubscriber implements EventHandler { throw new Error(`Unable to get idea by ID: ${event.payload.id}`) } - const targetAudiences = await this.repository.getTargetAudiencesByIdeaId( - idea.getId().getValue() - ) - - const audiences = targetAudiences.map((targetAudience) => ({ + const audiences = idea.getTargetAudiences().map((targetAudience) => ({ segment: targetAudience.getSegment(), description: targetAudience.getDescription(), challenges: targetAudience.getChallenges(), diff --git a/src/idea/events/subscribers/SWOTAnalysisEvaluationSubscriber.ts b/src/idea/events/subscribers/SWOTAnalysisEvaluationSubscriber.ts index e3ce905..edaebe5 100644 --- a/src/idea/events/subscribers/SWOTAnalysisEvaluationSubscriber.ts +++ b/src/idea/events/subscribers/SWOTAnalysisEvaluationSubscriber.ts @@ -61,26 +61,13 @@ export class SWOTAnalysisEvaluationSubscriber implements EventHandler { throw new Error(`Unable to get idea by ID: ${event.payload.id}`) } - const targetAudiences = await this.repository.getTargetAudiencesByIdeaId( - idea.getId().getValue() - ) - - if (targetAudiences.length === 0) { - throw new Error( - `Idea ${event.payload.id} does not have target audiences` - ) - } - - const audiences = targetAudiences.map((targetAudience) => ({ + const audiences = idea.getTargetAudiences().map((targetAudience) => ({ segment: targetAudience.getSegment(), description: targetAudience.getDescription(), challenges: targetAudience.getChallenges(), })) - const valueProposition = - await this.repository.getValuePropositionByIdeaId( - idea.getId().getValue() - ) + const valueProposition = idea.getValueProposition() if (!valueProposition) { throw new Error( @@ -101,7 +88,7 @@ export class SWOTAnalysisEvaluationSubscriber implements EventHandler { ) await this.repository.updateIdea(event.payload.id, (idea): Idea => { - idea.addSWOTAnalysis( + idea.setSWOTAnalysis( SWOTAnalysis.New( evaluation.strengths, evaluation.weaknesses, diff --git a/src/idea/events/subscribers/SocialMediaCampaignsSubscriber.ts b/src/idea/events/subscribers/SocialMediaCampaignsSubscriber.ts index b65daea..270dfa6 100644 --- a/src/idea/events/subscribers/SocialMediaCampaignsSubscriber.ts +++ b/src/idea/events/subscribers/SocialMediaCampaignsSubscriber.ts @@ -84,26 +84,13 @@ export class SocialMediaCampaignsSubscriber implements EventHandler { throw new Error(`Unable to get idea by ID: ${event.payload.id}`) } - const targetAudiences = await this.repository.getTargetAudiencesByIdeaId( - idea.getId().getValue() - ) - - if (targetAudiences.length === 0) { - throw new Error( - `Idea ${event.payload.id} does not have target audiences` - ) - } - - const audiences = targetAudiences.map((targetAudience) => ({ + const audiences = idea.getTargetAudiences().map((targetAudience) => ({ segment: targetAudience.getSegment(), description: targetAudience.getDescription(), challenges: targetAudience.getChallenges(), })) - const valueProposition = - await this.repository.getValuePropositionByIdeaId( - idea.getId().getValue() - ) + const valueProposition = idea.getValueProposition() if (!valueProposition) { throw new Error( @@ -156,7 +143,7 @@ export class SocialMediaCampaignsSubscriber implements EventHandler { }) await this.repository.updateIdea(event.payload.id, (idea): Idea => { - idea.addSocialMediaCampaigns(socialMediaCampaigns) + idea.setSocialMediaCampaigns(socialMediaCampaigns) return idea }) diff --git a/src/idea/events/subscribers/TargetAudienceEvaluationSubscriber.ts b/src/idea/events/subscribers/TargetAudienceEvaluationSubscriber.ts index f2b5369..bb3ae78 100644 --- a/src/idea/events/subscribers/TargetAudienceEvaluationSubscriber.ts +++ b/src/idea/events/subscribers/TargetAudienceEvaluationSubscriber.ts @@ -2,6 +2,8 @@ import * as Sentry from '@sentry/nextjs' import { Idea } from '@/idea/domain/Aggregate' import { Repository } from '@/idea/domain/Repository' import { IdeaCreated } from '@/idea/domain/events/IdeaCreated' +import { TargetAudiencesEvaluated } from '@/idea/domain/events/TargetAudiencesEvaluated' +import { EventBus } from '@/idea/events/EventBus' import { EventHandler } from '@/idea/events/EventHandler' type Evaluation = { @@ -25,7 +27,8 @@ export class TargetAudienceEvaluationSubscriber implements EventHandler { constructor( private readonly repository: Repository, - private readonly aiService: AIService + private readonly aiService: AIService, + private readonly eventBus: EventBus ) {} getName(): string { @@ -47,11 +50,7 @@ export class TargetAudienceEvaluationSubscriber implements EventHandler { throw new Error(`Unable to get idea by ID: ${event.payload.id}`) } - const targetAudiences = await this.repository.getTargetAudiencesByIdeaId( - idea.getId().getValue() - ) - - const tasks = targetAudiences.map((targetAudience) => + const tasks = idea.getTargetAudiences().map((targetAudience) => this.aiService .evaluateTargetAudience( idea.getId().getValue(), @@ -72,13 +71,13 @@ export class TargetAudienceEvaluationSubscriber implements EventHandler { await this.repository.updateIdea(event.payload.id, (idea): Idea => { evaluations.forEach((audience) => { - idea.addTargetAudience(audience) + idea.updateTargetAudience(audience) }) return idea }) - // TODO: Emit Event + this.eventBus.emit(new TargetAudiencesEvaluated(idea.getId().getValue())) } catch (e) { Sentry.captureException(e, { contexts: { diff --git a/src/idea/events/subscribers/ValuePropositionEvaluationSubscriber.ts b/src/idea/events/subscribers/ValuePropositionEvaluationSubscriber.ts index 9c9b518..aaec906 100644 --- a/src/idea/events/subscribers/ValuePropositionEvaluationSubscriber.ts +++ b/src/idea/events/subscribers/ValuePropositionEvaluationSubscriber.ts @@ -2,7 +2,7 @@ import * as Sentry from '@sentry/nextjs' import { Idea } from '@/idea/domain/Aggregate' import { Repository } from '@/idea/domain/Repository' import { ValueProposition } from '@/idea/domain/ValueProposition' -import { IdeaCreated } from '@/idea/domain/events/IdeaCreated' +import { TargetAudiencesEvaluated } from '@/idea/domain/events/TargetAudiencesEvaluated' import { ValuePropositionEvaluated } from '@/idea/domain/events/ValuePropositionEvaluated' import { EventBus } from '@/idea/events/EventBus' import { EventHandler } from '@/idea/events/EventHandler' @@ -40,7 +40,7 @@ export class ValuePropositionEvaluationSubscriber implements EventHandler { return ValuePropositionEvaluationSubscriber.className } - async handle(event: IdeaCreated): Promise { + async handle(event: TargetAudiencesEvaluated): Promise { Sentry.setTag('component', 'BackgroundJob') Sentry.setTag('job_type', this.getName()) Sentry.setTag('event_type', event.type) @@ -55,11 +55,7 @@ export class ValuePropositionEvaluationSubscriber implements EventHandler { throw new Error(`Unable to get idea by ID: ${event.payload.id}`) } - const targetAudiences = await this.repository.getTargetAudiencesByIdeaId( - idea.getId().getValue() - ) - - const audiences = targetAudiences.map((targetAudience) => ({ + const audiences = idea.getTargetAudiences().map((targetAudience) => ({ segment: targetAudience.getSegment(), description: targetAudience.getDescription(), challenges: targetAudience.getChallenges(), @@ -72,7 +68,7 @@ export class ValuePropositionEvaluationSubscriber implements EventHandler { ) await this.repository.updateIdea(event.payload.id, (idea): Idea => { - idea.addValueProposition( + idea.setValueProposition( ValueProposition.New( evaluation.mainBenefit, evaluation.problemSolving, diff --git a/src/idea/service/Service.ts b/src/idea/service/Service.ts index d1f410b..7b9720e 100644 --- a/src/idea/service/Service.ts +++ b/src/idea/service/Service.ts @@ -17,6 +17,10 @@ import { MakeReservationHandler } from '@/idea/app/commands/MakeReservation' import { RequestSocialMediaCampaignsHandler } from '@/idea/app/commands/RequestSocialMediaCampaigns' import { GetIdeaHandler } from '@/idea/app/queries/GetIdea' import { GetSocialMediaCampaignsHandler } from '@/idea/app/queries/GetSocialMediaCampaigns' +import { IdeaCreated } from '@/idea/domain/events/IdeaCreated' +import { SocialMediaCampaignsRequested } from '@/idea/domain/events/SocialMediaCampaignsRequested' +import { TargetAudiencesEvaluated } from '@/idea/domain/events/TargetAudiencesEvaluated' +import { ValuePropositionEvaluated } from '@/idea/domain/events/ValuePropositionEvaluated' import { CompetitorAnalysisEvaluationSubscriber } from '@/idea/events/subscribers/CompetitorAnalysisEvaluationSubscriber' import { ContentIdeasEvaluationSubscriber } from '@/idea/events/subscribers/ContentIdeasEvaluationSubscriber' import { ElevatorPitchesEvaluationSubscriber } from '@/idea/events/subscribers/ElevatorPitchesEvaluationSubscriber' @@ -37,7 +41,8 @@ const registerApp = (): Application => { const targetAudienceEvaluationSubscriber = new TargetAudienceEvaluationSubscriber( ideaRepository, - new TargetAudienceEvaluator(env.OPENAI_API_KEY) + new TargetAudienceEvaluator(env.OPENAI_API_KEY), + eventBus ) const valuePropositionEvaluationSubscriber = @@ -92,29 +97,41 @@ const registerApp = (): Application => { new SocialMediaCampaignsEvaluator(env.OPENAI_API_KEY) ) - eventBus.subscribe('IdeaCreated', targetAudienceEvaluationSubscriber) - eventBus.subscribe('IdeaCreated', valuePropositionEvaluationSubscriber) - eventBus.subscribe('IdeaCreated', marketAnalysisEvaluationSubscriber) - eventBus.subscribe('IdeaCreated', competitorAnalysisEvaluationSubscriber) - eventBus.subscribe('IdeaCreated', potentialNamesEvaluationSubscriber) + eventBus.subscribe(IdeaCreated.eventName, targetAudienceEvaluationSubscriber) + eventBus.subscribe( + TargetAudiencesEvaluated.eventName, + valuePropositionEvaluationSubscriber + ) + eventBus.subscribe( + TargetAudiencesEvaluated.eventName, + marketAnalysisEvaluationSubscriber + ) + eventBus.subscribe( + TargetAudiencesEvaluated.eventName, + competitorAnalysisEvaluationSubscriber + ) + eventBus.subscribe( + TargetAudiencesEvaluated.eventName, + potentialNamesEvaluationSubscriber + ) eventBus.subscribe( - 'ValuePropositionEvaluated', + ValuePropositionEvaluated.eventName, swotAnalysisEvaluationSubscriber ) eventBus.subscribe( - 'ValuePropositionEvaluated', + ValuePropositionEvaluated.eventName, elevatorPitchesEvaluationSubscriber ) eventBus.subscribe( - 'ValuePropositionEvaluated', + ValuePropositionEvaluated.eventName, googleTrendsKeywordsEvaluationSubscriber ) eventBus.subscribe( - 'ValuePropositionEvaluated', + ValuePropositionEvaluated.eventName, contentIdeasEvaluationSubscriber ) eventBus.subscribe( - 'SocialMediaCampaignsRequested', + SocialMediaCampaignsRequested.eventName, socialMediaCampaignsSubscriber )