bojanz / currency

Currency handling for Go.
https://pkg.go.dev/github.com/bojanz/currency
MIT License
557 stars 45 forks source link

Document some more features #8

Closed fgm closed 2 years ago

fgm commented 3 years ago

I think the README should document at least two more features in the code:

Alternatively, documenting a serialization that would allow SQL ordering would be nice too. I didn't find one in the current implementation, though.

bojanz commented 3 years ago

Thanks! Mentioning go-cmp somewhere would indeed be good.

What are the benefits of trying to use BinaryMarshaler for db storage?

My assumption was that anyone storing amounts in the database would use separate decimal and char fields for the number and currency_code. The binary marshal was implemented for exchange on the wire (e.g. the gob package).

I believe that if the format was reversed ("9.99USD" instead of "USD9.99") then MySQL could cast the value into a number for ordering, but I haven't explored the performance of that, nor how it would work in Postgres.

fgm commented 3 years ago

The idea is that not all databases support decimal in a portable way, AFAIK (maybe they now do ? it wasn't implemented in a portable way some years ago in SQL engines, even less in NoSQL): providing a string representation allows the type to be stored to just about anything without having to implement application-level mapping.

Using BinaryMarshaler for this is just a really simple way to do it, no special interest in it.

bojanz commented 3 years ago

The decimal type is a part of the SQL standard, and it's currently supported by all databases that I checked, so it's safe to recommend.

For cases where a decimal type is not available, there are two solid options:

This means that for proper sorting in any system, the primary thing to do is store the number and the currency code separately. Which means that we can probably keep the binary format as-is, but document storage recommendations. How does that sound?

fgm commented 3 years ago

Hadn't realized that Decimal was now usable across engines, so it is indeed enough to document it. Better have better doc than more code, I think.

Just to explain how this issue came: when drafting a homework subject for my students using monetary amounts, I wondered, thinking about how they'd have to store these data: "ok so we have currency.NewAmount" to create an Amount from a string. Now, what is the inverse function generating the same string I can pass to NewAmount again to obtain an identity when they get the result from the DB ?"

bojanz commented 3 years ago

Yeah, that's a valid question, it's not obvious at all. I'll see what I can come up with for the README

fgm commented 3 years ago

BTW, I double checked, and AIUI SQLite does not actually support DECIMAL: it only pretends to, by mapping it to its NUMERIC type, which uses a float if the value is not an integer. That's really something currencies want to avoid.

bojanz commented 3 years ago

Haven't forgotten about this. In the meantime, opened #9 to improve DX for Postgres users.

bojanz commented 3 years ago

Documented go-cmp and pgx integration in 7535b5417d25ed1f87167770a20c7adc53ad2dfb.

The next step will be to create a separate section for database integration, with integration notes for mysql (decimal column) and sqlite (int column). The int approach has been made possible by e18a43233c0ec25e3ad0d130e580d56d6d031158, since there are now methods for both getting the amount as an integer, and creating an amount from an integer.

bojanz commented 2 years ago

Database integration notes are now complete at https://github.com/bojanz/currency/wiki/Database-integration-notes

The SQLite section can be expanded with actual examples if someone's in the mood to give us an example schema.