diff --git a/internal/domain/entities/habit.go b/internal/domain/entities/habit.go new file mode 100644 index 0000000..0390197 --- /dev/null +++ b/internal/domain/entities/habit.go @@ -0,0 +1,48 @@ +package entities + +import ( + "time" + + "habit-tracker-api/internal/domain/value_objects" +) + +type Habit struct { + ID string + UserID string + Name string + Description string + Type value_objects.HabitType + Frequency value_objects.Frequency + SpecificDays []int + SpecificDates []int + CarryOver bool + TargetValue *float64 + CreatedAt time.Time + ArchivedAt *time.Time +} + +func NewHabit( + userID string, + name string, + habitType value_objects.HabitType, + frequency value_objects.Frequency, + carryOver bool, +) *Habit { + return &Habit{ + UserID: userID, + Name: name, + Type: habitType, + Frequency: frequency, + CarryOver: carryOver, + CreatedAt: time.Now(), + } +} + +func (h *Habit) Archive() { + now := time.Now() + h.ArchivedAt = &now +} + +func (h *Habit) IsActive() bool { + return h.ArchivedAt == nil +} diff --git a/internal/domain/entities/habit_entry.go b/internal/domain/entities/habit_entry.go new file mode 100644 index 0000000..7ca380b --- /dev/null +++ b/internal/domain/entities/habit_entry.go @@ -0,0 +1,30 @@ +package entities + +import "time" + +type HabitEntry struct { + ID string + HabitID string + ScheduledDate time.Time + CompletedAt time.Time + Value *float64 + DeletedAt *time.Time +} + +func NewHabitEntry(habitID string, scheduledDate time.Time, value *float64) *HabitEntry { + return &HabitEntry{ + HabitID: habitID, + ScheduledDate: scheduledDate, + CompletedAt: time.Now(), + Value: value, + } +} + +func (e *HabitEntry) Delete() { + now := time.Now() + e.DeletedAt = &now +} + +func (e *HabitEntry) IsDeleted() bool { + return e.DeletedAt != nil +} diff --git a/internal/domain/entities/user.go b/internal/domain/entities/user.go new file mode 100644 index 0000000..ea10fbd --- /dev/null +++ b/internal/domain/entities/user.go @@ -0,0 +1,23 @@ +package entities + +import "time" + +type User struct { + ID string + Email string + PasswordHash string + Timezone string + CreatedAt time.Time + UpdatedAt time.Time +} + +func NewUser(email, passwordHash, timezone string) *User { + now := time.Now() + return &User{ + Email: email, + PasswordHash: passwordHash, + Timezone: timezone, + CreatedAt: now, + UpdatedAt: now, + } +} diff --git a/internal/domain/repositories/habit_entry_repository.go b/internal/domain/repositories/habit_entry_repository.go new file mode 100644 index 0000000..fc15485 --- /dev/null +++ b/internal/domain/repositories/habit_entry_repository.go @@ -0,0 +1,18 @@ +package repositories + +import ( + "context" + "time" + + "habit-tracker-api/internal/domain/entities" +) + +type HabitEntryRepository interface { + Create(ctx context.Context, entry *entities.HabitEntry) error + FindByID(ctx context.Context, id string) (*entities.HabitEntry, error) + FindByHabitID(ctx context.Context, habitID string) ([]*entities.HabitEntry, error) + FindByHabitIDAndDateRange(ctx context.Context, habitID string, from, to time.Time) ([]*entities.HabitEntry, error) + FindPendingByHabitID(ctx context.Context, habitID string, beforeDate time.Time) ([]*entities.HabitEntry, error) + Update(ctx context.Context, entry *entities.HabitEntry) error + Delete(ctx context.Context, id string) error +} diff --git a/internal/domain/repositories/habit_repository.go b/internal/domain/repositories/habit_repository.go new file mode 100644 index 0000000..a758471 --- /dev/null +++ b/internal/domain/repositories/habit_repository.go @@ -0,0 +1,16 @@ +package repositories + +import ( + "context" + + "habit-tracker-api/internal/domain/entities" +) + +type HabitRepository interface { + Create(ctx context.Context, habit *entities.Habit) error + FindByID(ctx context.Context, id string) (*entities.Habit, error) + FindByUserID(ctx context.Context, userID string) ([]*entities.Habit, error) + FindActiveByUserID(ctx context.Context, userID string) ([]*entities.Habit, error) + Update(ctx context.Context, habit *entities.Habit) error + Delete(ctx context.Context, id string) error +} diff --git a/internal/domain/repositories/user_repository.go b/internal/domain/repositories/user_repository.go new file mode 100644 index 0000000..1a3372e --- /dev/null +++ b/internal/domain/repositories/user_repository.go @@ -0,0 +1,14 @@ +package repositories + +import ( + "context" + + "habit-tracker-api/internal/domain/entities" +) + +type UserRepository interface { + Create(ctx context.Context, user *entities.User) error + FindByID(ctx context.Context, id string) (*entities.User, error) + FindByEmail(ctx context.Context, email string) (*entities.User, error) + Update(ctx context.Context, user *entities.User) error +} diff --git a/internal/domain/value_objects/frequency.go b/internal/domain/value_objects/frequency.go new file mode 100644 index 0000000..f5c17d9 --- /dev/null +++ b/internal/domain/value_objects/frequency.go @@ -0,0 +1,17 @@ +package value_objects + +type Frequency string + +const ( + FrequencyDaily Frequency = "DAILY" + FrequencyWeekly Frequency = "WEEKLY" + FrequencyMonthly Frequency = "MONTHLY" +) + +func (f Frequency) IsValid() bool { + switch f { + case FrequencyDaily, FrequencyWeekly, FrequencyMonthly: + return true + } + return false +} diff --git a/internal/domain/value_objects/habit_type.go b/internal/domain/value_objects/habit_type.go new file mode 100644 index 0000000..baba5ff --- /dev/null +++ b/internal/domain/value_objects/habit_type.go @@ -0,0 +1,17 @@ +package value_objects + +type HabitType string + +const ( + HabitTypeBoolean HabitType = "BOOLEAN" + HabitTypeCounter HabitType = "COUNTER" + HabitTypeValue HabitType = "VALUE" +) + +func (ht HabitType) IsValid() bool { + switch ht { + case HabitTypeBoolean, HabitTypeCounter, HabitTypeValue: + return true + } + return false +} diff --git a/internal/shared/errors/errors.go b/internal/shared/errors/errors.go new file mode 100644 index 0000000..74e5884 --- /dev/null +++ b/internal/shared/errors/errors.go @@ -0,0 +1,11 @@ +package errors + +import "errors" + +var ( + ErrNotFound = errors.New("resource not found") + ErrAlreadyExists = errors.New("resource already exists") + ErrInvalidInput = errors.New("invalid input") + ErrUnauthorized = errors.New("unauthorized") + ErrInvalidCredentials = errors.New("invalid credentials") +)