// NewPostgresReadRepository creates a new PostgresReadRepository.
func NewPostgresReadRepository(conn, table string) (*PostgresReadRepository, error) {
	lgr := lager.Child()
	lgr.Set("table", table)

	db, err := initDB(conn)
	if err != nil {
		return nil, err
	}

	create := fmt.Sprintf(`
CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public;

CREATE TABLE IF NOT EXISTS %s (
  data jsonb
);
`, table)

	_, err = db.Exec(create)
	if err != nil {
		return nil, ErrCouldNotCreateTables
	}

	stmts := map[string]string{
		"save":    fmt.Sprintf("INSERT INTO %s (data) VALUES ($1)", table),
		"find":    fmt.Sprintf("SELECT * FROM %s WHERE data->>'id'=$1", table),
		"findall": fmt.Sprintf("SELECT * FROM %s", table),
		"remove":  fmt.Sprintf("DELETE FROM %s WHERE data->>'id'=$1", table),
		"clear":   fmt.Sprintf("DELETE FROM %s", table),
		"update":  fmt.Sprintf("UPDATE %s set data=$1 WHERE data->>'id'=$2", table),
	}

	return &PostgresReadRepository{
		db:    db,
		table: table,
		stmts: stmts,
		lgr:   lgr,
	}, nil
}
// NewPostgresEventStore creates a new PostgresEventStore.
func NewPostgresEventStore(eventBus EventBus, conn string) (*PostgresEventStore, error) {
	lgr := lager.Child()

	db, err := initDB(conn)
	if err != nil {
		lgr.WithError(err).Errorf("Unable to initialize database")
		return nil, err
	}

	if _, err = db.Exec(`
CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public;

CREATE TABLE IF NOT EXISTS aggregrates (
  id uuid NOT NULL,
  version int NOT NULL
);

CREATE TABLE IF NOT EXISTS events(
  aggregrateid uuid NOT NULL,
  type text,
  version int,
  timestamp timestamp without time zone default (now() at time zone 'utc'),
  data jsonb
)
    `); err != nil {
		db.Close()
		lgr.WithError(err).Errorf("Unable to initialize tables")
		return nil, ErrCouldNotCreateTables
	}

	return &PostgresEventStore{
		eventBus:  eventBus,
		db:        db,
		factories: make(map[string]func() Event),
		lgr:       lgr,
	}, nil
}
// NewRabbitMQCommandBus creates a new RabbitMQ command bus. amqpURI is the RabbitMQ
// URI for rabbitmq. app is provides a namespace for this application, allowing
// for multiple command buses to run on one RabbitMQ and not conflict with eachother.
// tag is used as the RabbitMQ consumer tag for this bus.
func NewRabbitMQCommandBus(amqpURI, app, tag string) (*RabbitMQCommandBus, error) {
	lgr := lager.Child()

	connection, err := amqp.Dial(amqpURI)
	if err != nil {
		lgr.WithError(err).Errorf("Error dialing URI: %s", amqpURI)
		return nil, fmt.Errorf("Dial err: %s", err)
	}

	channel, err := connection.Channel()
	if err != nil {
		connection.Close()
		lgr.WithError(err).Errorf("Error opening channel")
		return nil, fmt.Errorf("Channel: %s", err)
	}

	exchange := appExchange(app, commandExchange)
	queueName := appTagQueueName(app, tag, commandQueueName)

	if err := channel.ExchangeDeclare(
		exchange,            // name
		commandExchangeType, // type
		true,                // durable
		false,               // auto-deleted
		false,               // internal
		false,               // noWait
		nil,                 // arguments
	); err != nil {
		channel.Close()
		connection.Close()
		lgr.WithError(err).Errorf("Error declaring exchange :%s", exchange)
		return nil, fmt.Errorf("Exchange Declare: %s", err)
	}

	queue, err := channel.QueueDeclare(
		queueName, // name of the queue
		true,      // durable
		false,     // delete when usused
		false,     // exclusive
		false,     // noWait
		nil,       // arguments
	)
	if err != nil {
		channel.Close()
		connection.Close()
		lgr.WithError(err).Errorf("Error declaring queue %s", queueName)
		return nil, fmt.Errorf("Queue Declare: %s", err)
	}

	if err = channel.QueueBind(
		queue.Name, // name of the queue
		commandKey, // bindingKey
		exchange,   // sourceExchange
		false,      // noWait
		nil,        // arguments
	); err != nil {
		channel.Close()
		connection.Close()
		lgr.WithError(err).Errorf("Error binding queue %s", queueName)
		return nil, fmt.Errorf("Queue Bind: %s", err)
	}

	deliveries, err := channel.Consume(
		queue.Name, // name
		tag,        // consumerTag,
		false,      // noAck
		false,      // exclusive
		false,      // noLocal
		false,      // noWait
		nil,        // arguments
	)
	if err != nil {
		channel.Close()
		connection.Close()
		lgr.WithError(err).Errorf("Error consuming queue %s", queueName)
		return nil, fmt.Errorf("Queue Consume: %s", err)
	}

	bus := &RabbitMQCommandBus{
		conn:      connection,
		channel:   channel,
		exchange:  exchange,
		queue:     queueName,
		tag:       tag,
		handlers:  make(map[string]CommandHandler),
		factories: make(map[string]func() Command),
		done:      make(chan error),
		lgr:       lgr,
	}

	go bus.handleCommands(deliveries, bus.done)
	return bus, nil
}