Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ID foreign keys directly from clients or not? #31

Closed
frederikhors opened this issue Jun 27, 2021 · 2 comments
Closed

ID foreign keys directly from clients or not? #31

frederikhors opened this issue Jun 27, 2021 · 2 comments

Comments

@frederikhors
Copy link

frederikhors commented Jun 27, 2021

Prologue

Let's say I'm creating a simple invoice.

  • domain/invoice.go:
type Invoice struct {
	id        int
	createdAt time.Time
	updatedAt *time.Time
	date         time.Time
	note         *string
	number       int
	payed        bool
	amount       int
	customer     *Customer
	paymentType  *PaymentType
	tax          *Tax
	rows         []InvoiceRow
	// ... many others
}

func NewInvoice(
	id int,
	date time.Time,
	note *string,
	number int,
	payed bool,
	customer *Customer,
	paymentType *PaymentType,
	tax *Tax,
	rows []Row,
) (*Invoice, error) {
	i := &Invoice{
		date:         date,
		note:         note,
		number:       number,
		payed:        payed,
		customer:     customer,
		paymentType:  paymentType,
		tax:          tax,
		rows:         rows,
	}

	i.SetAmount()

	return i, nil
}

// ... all methods like ID(), Date() and so on...

// and methods that use the values of those structs, such as:

func (i *Invoice) SetAmount() {
	for x := range i.rows {
		i.amount += i.rows[x].Qty() * i.rows[x].Price()
	}

	i.amount += i.amount * i.Tax().Amount()
}

I'm using specific types for Invoice creation (directly from API port):

  • app/commands/types.go:
type CreateInvoiceRequest struct {
	Date          time.Time
	Note          *string
	Number        int
	Payed         bool
	CustomerID    int
	PaymentTypeID int
	TaxID         int
	Rows          []InvoiceRowInput
}

type InvoiceRowInput struct {
	ID          int
	Description *string
	Qty         int
	Price       int
	ProductID   *int
}

Then in my `app/command/invoice_create.go:

func (h CreateInvoiceHandler) Handle(ctx context.Context, createInvoiceRequest *CreateInvoiceRequest) (*domain.Invoice, error) {
	var domainInvoice *domain.Invoice

	domainInvoice, err := domain.NewInvoice(
		createInvoiceRequest.Date,
		createInvoiceRequest.Note,
		createInvoiceRequest.Number,
		createInvoiceRequest.Payed,
		createInvoiceRequest.CustomerID,
		createInvoiceRequest.PaymentTypeID,
		createInvoiceRequest.TaxID,
		createInvoiceRequest.Rows,
	)
	if err != nil {
		return nil, err
	}

	return h.repo.Create(ctx, createInvoiceRe)
}

Question

As you can see I'm getting from clients only the ID of customer, paymentType, tax, and so on...

But I use those structs value for my NewInvoice func.

Now I can fetch those values from repo using methods like yours:

UpdateTraining(
  ctx context.Context,
  trainingUUID string,
  user User,
  updateFn func(ctx context.Context, tr *Training) (*Training, error),
) error

in updateFn I can get each of them and only after I can use NewInvoice().

Or maybe I can change API and get that data from client (e.g.: Tax instead of taxID).

And maybe this is also a business doubt because maybe those ID references must be present and correct (in DB) at creation and not taken by the client itself or maybe not.

What do you think of how I am progressing? Do you have any other ideas?

Where am I doing wrong?

@m110
Copy link
Member

m110 commented Jun 29, 2021

Hey @frederikhors!

As I mentioned in the previous discussion, I don't like the idea of calling another service for all missing details. It creates coupling between the services, and you could leak too much domain knowledge, so watch out for it.

Or maybe I can change API and get that data from client (e.g.: Tax instead of taxID).

I think you could consider this, but there are some complexities as well. I'd try to get only what's needed for the invoice. And watch out for the language boundaries. You now pass a customer, which probably has many details your invoice doesn't need. The invoice should need only a "buyer" information that includes address and tax details.

Not sure what's in PaymentType and Tax, do you store these in a database?

@frederikhors
Copy link
Author

frederikhors commented Jun 29, 2021

Not sure what's in PaymentType and Tax, do you store these in a database?

Yes, my head is still DB-oriented unfortunately.

THANKS!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants