On Github xala3pa / Money-Talk-Greach-2016
https://en.wikipedia.org/wiki/Cohesion_(computer_science)
    
    [ 'totalPrice': CurrencyUtils.format( purchase.totalPrice, purchase.country ) ]
    
    CurrencyService currencyService
    
    BigDecimal totalPrice = currencyService.convertCurrency( purchase.totalPrice, 
                                purchase.currency, gateway.currency, round )    
    ['totalPrice': purchase.totalPrice.format( Locale.US )]
    Money totalPrice = purchase.totalPrice.exchangeTo( gateway.currency )http://www.tutorialspoint.com/cplusplus/cpp_data_encapsulation.htm
  @groovy.transform.CompileStatic
  final class Money implements Serializable, Comparable<Money>, MoneyExchange, MoneyFormat {
    
      final BigDecimal amount
      final Currency currency
      // ...
    
  }  
  @groovy.transform.PackageScope
  trait MoneyExchange {
    
          //...
      Money exchangeTo(Currency to, Exchange exchange = getCurrentExchange()) {
        
          //... 
      }
  }
  @groovy.transform.PackageScope
  trait MoneyFormat {
      //...
      String format(Locale locale = Locale.default) {
          
          //...
      }
  }http://c2.com/cgi/wiki?PrimitiveObsession
https://sourcemaking.com/refactoring/smells/primitive-obsession
https://sourcemaking.com/refactoring/smells/primitive-obsession
  class Ticket implements Serializable {
    
      BigDecimal totalPrice
      Currency currency
      //...
  }
  class Ticket implements Serializable {
    
      Money totalPrice
      //...
  }https://sourcemaking.com/refactoring/smells/feature-envy
https://sourcemaking.com/refactoring/smells/feature-envy
https://sourcemaking.com/refactoring/smells/feature-envy
https://sourcemaking.com/refactoring/smells/feature-envy
  
  
  BigDecimal totalPrice = currencyService.convertCurrency( purchase.totalPrice, 
                                purchase.currency, gateway.currency, round )Money totalPrice = purchase.totalPrice.exchangeTo( gateway.currency )
  @groovy.transform.CompileStatic
  final class Money implements Serializable, Comparable<Money>, MoneyExchange, MoneyFormat {
      final BigDecimal amount
      final Currency currency
      private final static MathContext MONETARY_CONTEXT = MathContext.DECIMAL128
      final static class CurrencyMismatchException extends RuntimeException {
          CurrencyMismatchException(String aMessage) {
              super(aMessage)
          }
      }
    
           //...
      Money(Number amount, Currency currency) {
          this.amount = (BigDecimal) amount
          this.currency = currency
      }
            //...    
 }
    class MoneyUserType implements UserType, ParameterizedType {
        private final static String DEFAULT_CURRENCY_COLUMN = 'currency'
        private final static int[] SQL_TYPES = [Types.DECIMAL] as int[]
        Properties parameterValues
        //...
        Object nullSafeGet(ResultSet rs, String[] names, Object owner) {
            //...
        }
        void nullSafeSet(PreparedStatement st, Object value, int index) {
            //...
        }
        Class returnedClass() {
            Money.class
        }
        int[] sqlTypes() {
            SQL_TYPES
        }
    }
    class Ticket implements Serializable {
        Money totalPrice
	
        //...
    
        static mapping = {
            totalPrice  type: MoneyUserType, params: [currencyColumn: 'divisa']
        }
    }
    trait MoneyExchange {
    
        //...
        Money exchangeTo(Currency to, Exchange exchange = getCurrentExchange()) {
        
            //...
        }
    }
    interface Exchange {
        BigDecimal getRate(Currency from, Currency to)
    }
Text
    trait MoneyFormat {
        //...
        String format(Locale locale = Locale.default) {
        
            //...
        }
        //...
    }
    
 class StructuredMoneyEditor extends AbstractStructuredBindingEditor<Money> {
   private static final String currencyPlaceholder = '¤'
   Money getPropertyValue(Map values) {
       DecimalFormat formatter = getCustomDecimalFormatter(values)
       BigDecimal parsedAmount = getParsedAmount(formatter, (String) values.amount)
       new Money(parsedAmount, (String) values.currency)
   }
   //...
 } 
    def doWithSpring = {
    
        //Custom structured property editor data binding for Money type
        moneyEditor com.ticketbis.money.StructuredMoneyEditor
    }
 
  class GreaterThanZeroConstraint extends AbstractConstraint {
      private static final String DEFAULT_INVALID_MESSAGE_CODE = 'default.gtZero.invalid'
      static final String CONSTRAINT_NAME = 'gtZero'
      private boolean gtZero
      //...
      protected void processValidate(Object target, Object propertyValue, Errors errors) {
          if (!validate(propertyValue)) {
              def args = (Object[]) [constraintPropertyName, 
                                        constraintOwningClass, propertyValue]
              rejectValue(target, errors,    
                    DEFAULT_INVALID_MESSAGE_CODE, "not.${CONSTRAINT_NAME}", args)
          }
      }
      //...
  } 
    def doWithSpring = {
        
        //...
        ConstrainedProperty.registerNewConstraint(
            GreaterThanZeroConstraint.CONSTRAINT_NAME,
            GreaterThanZeroConstraint)
        //...
    } 
    class Ticket implements Serializable {
    
        Money totalPrice
        
        //...
        static constraints = {
            totalPrice( nullable: false, gtZero: true )
        
            //...
        }     
    class MoneyTagLib {
        static namespace = 'money'
        def inputField = { attrs ->
            def name = attrs.remove('name')
            def value = attrs.remove('value')
            //...
        }
        def format = { attrs ->
            Money value = new Money(attrs.value)
            //...
        }
    }
     
    <money:inputField name="totalPrice" value="123.45" currency="EUR"/>
    <money:inputField name="totalPrice" value="${ money }"/>
    <money:format value="${ money }" pattern="¤ ##,##0.00"/>
    <money:format value="${ money }" numberFormat="${ formatter }"/>