diff --git a/packages/binary/datastructure/orderedmap/orderedmap.go b/packages/binary/datastructure/orderedmap/orderedmap.go
index fe0555421f1a5146b3f63e863c93d68192a033e5..8bc97443bfbf03919be8ad952fb634117ef01ec8 100644
--- a/packages/binary/datastructure/orderedmap/orderedmap.go
+++ b/packages/binary/datastructure/orderedmap/orderedmap.go
@@ -66,20 +66,22 @@ func (orderedMap *OrderedMap) Set(key interface{}, newValue interface{}) bool {
 	return true
 }
 
-func (orderedMap *OrderedMap) ForEach(consumer func(key, value interface{}) bool) {
+func (orderedMap *OrderedMap) ForEach(consumer func(key, value interface{}) bool) bool {
 	orderedMap.mutex.RLock()
 	currentEntry := orderedMap.head
 	orderedMap.mutex.RUnlock()
 
 	for currentEntry != nil {
 		if !consumer(currentEntry.key, currentEntry.value) {
-			return
+			return false
 		}
 
 		orderedMap.mutex.RLock()
 		currentEntry = currentEntry.next
 		orderedMap.mutex.RUnlock()
 	}
+
+	return true
 }
 
 func (orderedMap *OrderedMap) Delete(key interface{}) bool {
diff --git a/packages/binary/spammer/spammer.go b/packages/binary/spammer/spammer.go
index e59cf9df343a5d1451a3625c3cc846834ffb7fc1..ea0efc5ac16b3f4ca84dcf3f4740ca7cec0a3f26 100644
--- a/packages/binary/spammer/spammer.go
+++ b/packages/binary/spammer/spammer.go
@@ -7,8 +7,8 @@ import (
 	"github.com/iotaledger/hive.go/types"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/tipselector"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser"
 )
@@ -53,7 +53,7 @@ func (spammer *Spammer) run(tps int, processId int64) {
 
 		trunkTransactionId, branchTransactionId := spammer.tipSelector.GetTips()
 		spammer.transactionParser.Parse(
-			transaction.New(trunkTransactionId, branchTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM"))).Bytes(),
+			message.New(trunkTransactionId, branchTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM"))).Bytes(),
 			nil,
 		)
 
@@ -75,7 +75,7 @@ func (spammer *Spammer) run(tps int, processId int64) {
 func (spammer *Spammer) sendBurst(transactions int, processId int64) {
 	spammingIdentity := ed25119.GenerateKeyPair()
 
-	previousTransactionId := transaction.EmptyId
+	previousTransactionId := message.EmptyId
 
 	burstBuffer := make([][]byte, transactions)
 	for i := 0; i < transactions; i++ {
@@ -83,7 +83,7 @@ func (spammer *Spammer) sendBurst(transactions int, processId int64) {
 			return
 		}
 
-		spamTransaction := transaction.New(previousTransactionId, previousTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM")))
+		spamTransaction := message.New(previousTransactionId, previousTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM")))
 		previousTransactionId = spamTransaction.GetId()
 		burstBuffer[i] = spamTransaction.Bytes()
 	}
diff --git a/packages/binary/tangle/events.go b/packages/binary/tangle/events.go
index ebf537c9d1a761177779f9fc3660bcdd1a426362..ec056169f8f0ceddaaf3a3d4b344218973da00cc 100644
--- a/packages/binary/tangle/events.go
+++ b/packages/binary/tangle/events.go
@@ -3,7 +3,7 @@ package tangle
 import (
 	"github.com/iotaledger/hive.go/events"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 )
 
@@ -29,12 +29,12 @@ func newEvents() *Events {
 }
 
 func transactionIdEvent(handler interface{}, params ...interface{}) {
-	handler.(func(transaction.Id))(params[0].(transaction.Id))
+	handler.(func(message.Id))(params[0].(message.Id))
 }
 
 func cachedTransactionEvent(handler interface{}, params ...interface{}) {
-	handler.(func(*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))(
-		params[0].(*transaction.CachedTransaction).Retain(),
+	handler.(func(*message.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))(
+		params[0].(*message.CachedTransaction).Retain(),
 		params[1].(*transactionmetadata.CachedTransactionMetadata).Retain(),
 	)
 }
diff --git a/packages/binary/tangle/model/approver/approver.go b/packages/binary/tangle/model/approver/approver.go
index b08bf342d269616674e883d31791650dcdd29006..23c0842be18b328762caafb230a0025868b4ca8b 100644
--- a/packages/binary/tangle/model/approver/approver.go
+++ b/packages/binary/tangle/model/approver/approver.go
@@ -3,36 +3,36 @@ package approver
 import (
 	"github.com/iotaledger/hive.go/objectstorage"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 )
 
 type Approver struct {
 	objectstorage.StorableObjectFlags
 
 	storageKey            []byte
-	referencedTransaction transaction.Id
-	approvingTransaction  transaction.Id
+	referencedTransaction message.Id
+	approvingTransaction  message.Id
 }
 
-func New(referencedTransaction transaction.Id, approvingTransaction transaction.Id) *Approver {
+func New(referencedTransaction message.Id, approvingTransaction message.Id) *Approver {
 	approver := &Approver{
-		storageKey:            make([]byte, transaction.IdLength+transaction.IdLength),
+		storageKey:            make([]byte, message.IdLength+message.IdLength),
 		referencedTransaction: referencedTransaction,
 		approvingTransaction:  approvingTransaction,
 	}
 
-	copy(approver.storageKey[:transaction.IdLength], referencedTransaction[:])
-	copy(approver.storageKey[transaction.IdLength:], approvingTransaction[:])
+	copy(approver.storageKey[:message.IdLength], referencedTransaction[:])
+	copy(approver.storageKey[message.IdLength:], approvingTransaction[:])
 
 	return approver
 }
 
 func FromStorage(id []byte) (result objectstorage.StorableObject) {
 	approver := &Approver{
-		storageKey: make([]byte, transaction.IdLength+transaction.IdLength),
+		storageKey: make([]byte, message.IdLength+message.IdLength),
 	}
-	copy(approver.referencedTransaction[:], id[:transaction.IdLength])
-	copy(approver.approvingTransaction[:], id[transaction.IdLength:])
+	copy(approver.referencedTransaction[:], id[:message.IdLength])
+	copy(approver.approvingTransaction[:], id[message.IdLength:])
 	copy(approver.storageKey, id)
 
 	return approver
@@ -42,7 +42,7 @@ func (approver *Approver) GetStorageKey() []byte {
 	return approver.storageKey
 }
 
-func (approver *Approver) GetApprovingTransactionId() transaction.Id {
+func (approver *Approver) GetApprovingTransactionId() message.Id {
 	return approver.approvingTransaction
 }
 
diff --git a/packages/binary/tangle/model/transaction/cached_transaction.go b/packages/binary/tangle/model/message/cached_transaction.go
similarity index 97%
rename from packages/binary/tangle/model/transaction/cached_transaction.go
rename to packages/binary/tangle/model/message/cached_transaction.go
index d95494ed6e8706a0b2708a963b17cce3925461f5..8657ecb87618a106654fbba2acb861594406eb35 100644
--- a/packages/binary/tangle/model/transaction/cached_transaction.go
+++ b/packages/binary/tangle/model/message/cached_transaction.go
@@ -1,4 +1,4 @@
-package transaction
+package message
 
 import (
 	"github.com/iotaledger/hive.go/objectstorage"
diff --git a/packages/binary/tangle/model/transaction/id.go b/packages/binary/tangle/model/message/id.go
similarity index 98%
rename from packages/binary/tangle/model/transaction/id.go
rename to packages/binary/tangle/model/message/id.go
index c7a9319090879ecaa52812d2887c72a52dca71d7..4060031583911101ef10ab67c45932a86a56e530 100644
--- a/packages/binary/tangle/model/transaction/id.go
+++ b/packages/binary/tangle/model/message/id.go
@@ -1,4 +1,4 @@
-package transaction
+package message
 
 import (
 	"fmt"
diff --git a/packages/binary/tangle/model/message/init.go b/packages/binary/tangle/model/message/init.go
new file mode 100644
index 0000000000000000000000000000000000000000..15999765139b3e31a3828cb69e25b93d9c2c24eb
--- /dev/null
+++ b/packages/binary/tangle/model/message/init.go
@@ -0,0 +1,10 @@
+package message
+
+import (
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
+)
+
+func init() {
+	payload.SetGenericUnmarshalerFactory(data.GenericPayloadUnmarshalerFactory)
+}
diff --git a/packages/binary/tangle/model/transaction/payload/data/data.go b/packages/binary/tangle/model/message/payload/data/data.go
similarity index 96%
rename from packages/binary/tangle/model/transaction/payload/data/data.go
rename to packages/binary/tangle/model/message/payload/data/data.go
index 3c971c20c30459ef8d84d1ce2c3c012309e36409..70efdcfcdaa7b98f2171108ba85cab469851d018 100644
--- a/packages/binary/tangle/model/transaction/payload/data/data.go
+++ b/packages/binary/tangle/model/message/payload/data/data.go
@@ -4,7 +4,7 @@ import (
 	"github.com/iotaledger/hive.go/stringify"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
 )
 
 type Data struct {
diff --git a/packages/binary/tangle/model/transaction/payload/data/init.go b/packages/binary/tangle/model/message/payload/data/init.go
similarity index 56%
rename from packages/binary/tangle/model/transaction/payload/data/init.go
rename to packages/binary/tangle/model/message/payload/data/init.go
index 547bca2c68e3ad3ac12adc1b97ab82ae3878a764..f1ae7c9d143c2115e60d3d082c22584d56b170da 100644
--- a/packages/binary/tangle/model/transaction/payload/data/init.go
+++ b/packages/binary/tangle/model/message/payload/data/init.go
@@ -1,7 +1,7 @@
 package data
 
 import (
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
 )
 
 func init() {
diff --git a/packages/binary/tangle/model/transaction/payload/id.go b/packages/binary/tangle/model/message/payload/id.go
similarity index 100%
rename from packages/binary/tangle/model/transaction/payload/id.go
rename to packages/binary/tangle/model/message/payload/id.go
diff --git a/packages/binary/tangle/model/transaction/payload/payload.go b/packages/binary/tangle/model/message/payload/payload.go
similarity index 100%
rename from packages/binary/tangle/model/transaction/payload/payload.go
rename to packages/binary/tangle/model/message/payload/payload.go
diff --git a/packages/binary/tangle/model/transaction/payload/type.go b/packages/binary/tangle/model/message/payload/type.go
similarity index 100%
rename from packages/binary/tangle/model/transaction/payload/type.go
rename to packages/binary/tangle/model/message/payload/type.go
diff --git a/packages/binary/tangle/model/transaction/payload/type_register.go b/packages/binary/tangle/model/message/payload/type_register.go
similarity index 100%
rename from packages/binary/tangle/model/transaction/payload/type_register.go
rename to packages/binary/tangle/model/message/payload/type_register.go
diff --git a/packages/binary/tangle/model/transaction/test/transaction_test.go b/packages/binary/tangle/model/message/test/transaction_test.go
similarity index 65%
rename from packages/binary/tangle/model/transaction/test/transaction_test.go
rename to packages/binary/tangle/model/message/test/transaction_test.go
index 172e6509428b25e2803ebe9f738153b5b4772221..357ec1ba1978454ed0581d9835c55e1f79972d00 100644
--- a/packages/binary/tangle/model/transaction/test/transaction_test.go
+++ b/packages/binary/tangle/model/message/test/transaction_test.go
@@ -11,8 +11,8 @@ import (
 	"github.com/panjf2000/ants/v2"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
 )
 
 func BenchmarkVerifyDataTransactions(b *testing.B) {
@@ -21,7 +21,7 @@ func BenchmarkVerifyDataTransactions(b *testing.B) {
 
 	transactions := make([][]byte, b.N)
 	for i := 0; i < b.N; i++ {
-		tx := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data")))
+		tx := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data")))
 
 		if marshaledTransaction, err := tx.MarshalBinary(); err != nil {
 			b.Error(err)
@@ -35,7 +35,7 @@ func BenchmarkVerifyDataTransactions(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		currentIndex := i
 		pool.Submit(func() {
-			if tx, err, _ := transaction.FromBytes(transactions[currentIndex]); err != nil {
+			if tx, err, _ := message.FromBytes(transactions[currentIndex]); err != nil {
 				b.Error(err)
 			} else {
 				tx.VerifySignature()
@@ -49,9 +49,9 @@ func BenchmarkVerifyDataTransactions(b *testing.B) {
 func BenchmarkVerifySignature(b *testing.B) {
 	pool, _ := ants.NewPool(80, ants.WithNonblocking(false))
 
-	transactions := make([]*transaction.Transaction, b.N)
+	transactions := make([]*message.Transaction, b.N)
 	for i := 0; i < b.N; i++ {
-		transactions[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("test")))
+		transactions[i] = message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("test")))
 		transactions[i].Bytes()
 	}
 
diff --git a/packages/binary/tangle/model/transaction/transaction.go b/packages/binary/tangle/model/message/transaction.go
similarity index 98%
rename from packages/binary/tangle/model/transaction/transaction.go
rename to packages/binary/tangle/model/message/transaction.go
index d24202247602394f82ddf028ba65bc452384f723..ff3c9c362cdcc4f42e6fb9c6a00d40e9454cd84b 100644
--- a/packages/binary/tangle/model/transaction/transaction.go
+++ b/packages/binary/tangle/model/message/transaction.go
@@ -1,4 +1,4 @@
-package transaction
+package message
 
 import (
 	"sync"
@@ -8,7 +8,7 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
 
 	"github.com/iotaledger/hive.go/objectstorage"
 
diff --git a/packages/binary/tangle/model/missingtransaction/missingtransaction.go b/packages/binary/tangle/model/missingtransaction/missingtransaction.go
index ea1715c8d0a127324dd5f6a82ac9b2e2719bd0c0..64baa95c546c79f158ae50f4ec14fd4dcfc6b722 100644
--- a/packages/binary/tangle/model/missingtransaction/missingtransaction.go
+++ b/packages/binary/tangle/model/missingtransaction/missingtransaction.go
@@ -3,18 +3,18 @@ package missingtransaction
 import (
 	"time"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/hive.go/objectstorage"
 )
 
 type MissingTransaction struct {
 	objectstorage.StorableObjectFlags
 
-	transactionId transaction.Id
+	transactionId message.Id
 	missingSince  time.Time
 }
 
-func New(transactionId transaction.Id) *MissingTransaction {
+func New(transactionId message.Id) *MissingTransaction {
 	return &MissingTransaction{
 		transactionId: transactionId,
 		missingSince:  time.Now(),
@@ -28,7 +28,7 @@ func FromStorage(key []byte) objectstorage.StorableObject {
 	return result
 }
 
-func (missingTransaction *MissingTransaction) GetTransactionId() transaction.Id {
+func (missingTransaction *MissingTransaction) GetTransactionId() message.Id {
 	return missingTransaction.transactionId
 }
 
diff --git a/packages/binary/tangle/model/transaction/init.go b/packages/binary/tangle/model/transaction/init.go
deleted file mode 100644
index 5805588c0b6309d978159aa094c6c4966f73a8c9..0000000000000000000000000000000000000000
--- a/packages/binary/tangle/model/transaction/init.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package transaction
-
-import (
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
-)
-
-func init() {
-	payload.SetGenericUnmarshalerFactory(data.GenericPayloadUnmarshalerFactory)
-}
diff --git a/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go b/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go
index a7730e6224e9a4923b2bb0a2c3494aaca7c7300a..1fcf1c255093cb2e9f7dec98fae7426e05ed33ec 100644
--- a/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go
+++ b/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go
@@ -6,13 +6,13 @@ import (
 
 	"github.com/iotaledger/hive.go/objectstorage"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 )
 
 type TransactionMetadata struct {
 	objectstorage.StorableObjectFlags
 
-	transactionId      transaction.Id
+	transactionId      message.Id
 	receivedTime       time.Time
 	solid              bool
 	solidificationTime time.Time
@@ -21,7 +21,7 @@ type TransactionMetadata struct {
 	solidificationTimeMutex sync.RWMutex
 }
 
-func New(transactionId transaction.Id) *TransactionMetadata {
+func New(transactionId message.Id) *TransactionMetadata {
 	return &TransactionMetadata{
 		transactionId: transactionId,
 		receivedTime:  time.Now(),
diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go
index 0e86da66147ef245bef1599abf97c047a38a2fa9..ac2418daf5b7619ff920a2029a840f01af177b67 100644
--- a/packages/binary/tangle/tangle.go
+++ b/packages/binary/tangle/tangle.go
@@ -9,8 +9,8 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/approver"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/missingtransaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 
 	"github.com/iotaledger/hive.go/async"
@@ -41,9 +41,9 @@ type Tangle struct {
 func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) {
 	result = &Tangle{
 		storageId:                  storageId,
-		transactionStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransaction...), transaction.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
+		transactionStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransaction...), message.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
 		transactionMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransactionMetadata...), transactionmetadata.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
-		approverStorage:            objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), approver.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.PartitionKey(transaction.IdLength, transaction.IdLength), objectstorage.LeakDetectionEnabled(false)),
+		approverStorage:            objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), approver.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.PartitionKey(message.IdLength, message.IdLength), objectstorage.LeakDetectionEnabled(false)),
 		missingTransactionsStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleMissingTransaction...), missingtransaction.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)),
 
 		Events: *newEvents(),
@@ -60,22 +60,22 @@ func (tangle *Tangle) GetStorageId() []byte {
 }
 
 // Attaches a new transaction to the tangle.
-func (tangle *Tangle) AttachTransaction(transaction *transaction.Transaction) {
+func (tangle *Tangle) AttachTransaction(transaction *message.Transaction) {
 	tangle.storeTransactionsWorkerPool.Submit(func() { tangle.storeTransactionWorker(transaction) })
 }
 
 // Retrieves a transaction from the tangle.
-func (tangle *Tangle) GetTransaction(transactionId transaction.Id) *transaction.CachedTransaction {
-	return &transaction.CachedTransaction{CachedObject: tangle.transactionStorage.Load(transactionId[:])}
+func (tangle *Tangle) GetTransaction(transactionId message.Id) *message.CachedTransaction {
+	return &message.CachedTransaction{CachedObject: tangle.transactionStorage.Load(transactionId[:])}
 }
 
 // Retrieves the metadata of a transaction from the tangle.
-func (tangle *Tangle) GetTransactionMetadata(transactionId transaction.Id) *transactionmetadata.CachedTransactionMetadata {
+func (tangle *Tangle) GetTransactionMetadata(transactionId message.Id) *transactionmetadata.CachedTransactionMetadata {
 	return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Load(transactionId[:])}
 }
 
 // GetApprovers retrieves the approvers of a transaction from the tangle.
-func (tangle *Tangle) GetApprovers(transactionId transaction.Id) approver.CachedApprovers {
+func (tangle *Tangle) GetApprovers(transactionId message.Id) approver.CachedApprovers {
 	approvers := make(approver.CachedApprovers, 0)
 	tangle.approverStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool {
 		approvers = append(approvers, &approver.CachedApprover{CachedObject: cachedObject})
@@ -87,8 +87,8 @@ func (tangle *Tangle) GetApprovers(transactionId transaction.Id) approver.Cached
 }
 
 // Deletes a transaction from the tangle (i.e. for local snapshots)
-func (tangle *Tangle) DeleteTransaction(transactionId transaction.Id) {
-	tangle.GetTransaction(transactionId).Consume(func(currentTransaction *transaction.Transaction) {
+func (tangle *Tangle) DeleteTransaction(transactionId message.Id) {
+	tangle.GetTransaction(transactionId).Consume(func(currentTransaction *message.Transaction) {
 		trunkTransactionId := currentTransaction.GetTrunkTransactionId()
 		tangle.deleteApprover(trunkTransactionId, transactionId)
 
@@ -135,13 +135,13 @@ func (tangle *Tangle) Prune() error {
 }
 
 // Worker that stores the transactions and calls the corresponding storage events"
-func (tangle *Tangle) storeTransactionWorker(tx *transaction.Transaction) {
+func (tangle *Tangle) storeTransactionWorker(tx *message.Transaction) {
 	// store transaction
-	var cachedTransaction *transaction.CachedTransaction
+	var cachedTransaction *message.CachedTransaction
 	if _tmp, transactionIsNew := tangle.transactionStorage.StoreIfAbsent(tx); !transactionIsNew {
 		return
 	} else {
-		cachedTransaction = &transaction.CachedTransaction{CachedObject: _tmp}
+		cachedTransaction = &message.CachedTransaction{CachedObject: _tmp}
 	}
 
 	// store transaction metadata
@@ -170,9 +170,9 @@ func (tangle *Tangle) storeTransactionWorker(tx *transaction.Transaction) {
 }
 
 // Worker that solidifies the transactions (recursively from past to present).
-func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
-	isTransactionMarkedAsSolid := func(transactionId transaction.Id) bool {
-		if transactionId == transaction.EmptyId {
+func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	isTransactionMarkedAsSolid := func(transactionId message.Id) bool {
+		if transactionId == message.EmptyId {
 			return true
 		}
 
@@ -198,7 +198,7 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *transaction.C
 		return true
 	}
 
-	isTransactionSolid := func(transaction *transaction.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool {
+	isTransactionSolid := func(transaction *message.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool {
 		if transaction == nil || transaction.IsDeleted() {
 			return false
 		}
@@ -214,13 +214,13 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *transaction.C
 		return isTransactionMarkedAsSolid(transaction.GetTrunkTransactionId()) && isTransactionMarkedAsSolid(transaction.GetBranchTransactionId())
 	}
 
-	popElementsFromStack := func(stack *list.List) (*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata) {
+	popElementsFromStack := func(stack *list.List) (*message.CachedTransaction, *transactionmetadata.CachedTransactionMetadata) {
 		currentSolidificationEntry := stack.Front()
 		currentCachedTransaction := currentSolidificationEntry.Value.([2]interface{})[0]
 		currentCachedTransactionMetadata := currentSolidificationEntry.Value.([2]interface{})[1]
 		stack.Remove(currentSolidificationEntry)
 
-		return currentCachedTransaction.(*transaction.CachedTransaction), currentCachedTransactionMetadata.(*transactionmetadata.CachedTransactionMetadata)
+		return currentCachedTransaction.(*message.CachedTransaction), currentCachedTransactionMetadata.(*transactionmetadata.CachedTransactionMetadata)
 	}
 
 	// initialize the stack
@@ -261,9 +261,9 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *transaction.C
 }
 
 // Worker that Monitors the missing transactions (by scheduling regular checks).
-func (tangle *Tangle) monitorMissingTransactionWorker(transactionId transaction.Id) {
-	var scheduleNextMissingCheck func(transactionId transaction.Id)
-	scheduleNextMissingCheck = func(transactionId transaction.Id) {
+func (tangle *Tangle) monitorMissingTransactionWorker(transactionId message.Id) {
+	var scheduleNextMissingCheck func(transactionId message.Id)
+	scheduleNextMissingCheck = func(transactionId message.Id) {
 		time.AfterFunc(MISSING_CHECK_INTERVAL, func() {
 			tangle.missingTransactionsStorage.Load(transactionId[:]).Consume(func(object objectstorage.StorableObject) {
 				missingTransaction := object.(*missingtransaction.MissingTransaction)
@@ -288,24 +288,24 @@ func (tangle *Tangle) monitorMissingTransactionWorker(transactionId transaction.
 	scheduleNextMissingCheck(transactionId)
 }
 
-func (tangle *Tangle) deleteApprover(approvedTransaction transaction.Id, approvingTransaction transaction.Id) {
-	idToDelete := make([]byte, transaction.IdLength+transaction.IdLength)
-	copy(idToDelete[:transaction.IdLength], approvedTransaction[:])
-	copy(idToDelete[transaction.IdLength:], approvingTransaction[:])
+func (tangle *Tangle) deleteApprover(approvedTransaction message.Id, approvingTransaction message.Id) {
+	idToDelete := make([]byte, message.IdLength+message.IdLength)
+	copy(idToDelete[:message.IdLength], approvedTransaction[:])
+	copy(idToDelete[message.IdLength:], approvingTransaction[:])
 	tangle.approverStorage.Delete(idToDelete)
 }
 
 // Deletes a transaction and all of its approvers (recursively).
-func (tangle *Tangle) deleteSubtangle(transactionId transaction.Id) {
+func (tangle *Tangle) deleteSubtangle(transactionId message.Id) {
 	cleanupStack := list.New()
 	cleanupStack.PushBack(transactionId)
 
-	processedTransactions := make(map[transaction.Id]types.Empty)
+	processedTransactions := make(map[message.Id]types.Empty)
 	processedTransactions[transactionId] = types.Void
 
 	for cleanupStack.Len() >= 1 {
 		currentStackEntry := cleanupStack.Front()
-		currentTransactionId := currentStackEntry.Value.(transaction.Id)
+		currentTransactionId := currentStackEntry.Value.(message.Id)
 		cleanupStack.Remove(currentStackEntry)
 
 		tangle.DeleteTransaction(currentTransactionId)
diff --git a/packages/binary/tangle/tangle_test.go b/packages/binary/tangle/tangle_test.go
index 6ba20cc720c737df8127ccf2322ec1a9599ea4dc..9cf607e6dcec5c78cf853c5f4ccaf0865b9cbabf 100644
--- a/packages/binary/tangle/tangle_test.go
+++ b/packages/binary/tangle/tangle_test.go
@@ -11,8 +11,8 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/database"
 	"github.com/iotaledger/goshimmer/plugins/config"
@@ -34,9 +34,9 @@ func BenchmarkTangle_AttachTransaction(b *testing.B) {
 
 	testIdentity := ed25119.GenerateKeyPair()
 
-	transactionBytes := make([]*transaction.Transaction, b.N)
+	transactionBytes := make([]*message.Transaction, b.N)
 	for i := 0; i < b.N; i++ {
-		transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, testIdentity, time.Now(), 0, data.New([]byte("some data")))
+		transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, testIdentity, time.Now(), 0, data.New([]byte("some data")))
 		transactionBytes[i].Bytes()
 	}
 
@@ -63,36 +63,36 @@ func TestTangle_AttachTransaction(t *testing.T) {
 		return
 	}
 
-	tangle.Events.TransactionAttached.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	tangle.Events.TransactionAttached.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
 		cachedTransactionMetadata.Release()
 
-		cachedTransaction.Consume(func(transaction *transaction.Transaction) {
+		cachedTransaction.Consume(func(transaction *message.Transaction) {
 			fmt.Println("ATTACHED:", transaction.GetId())
 		})
 	}))
 
-	tangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	tangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
 		cachedTransactionMetadata.Release()
 
-		cachedTransaction.Consume(func(transaction *transaction.Transaction) {
+		cachedTransaction.Consume(func(transaction *message.Transaction) {
 			fmt.Println("SOLID:", transaction.GetId())
 		})
 	}))
 
-	tangle.Events.TransactionUnsolidifiable.Attach(events.NewClosure(func(transactionId transaction.Id) {
+	tangle.Events.TransactionUnsolidifiable.Attach(events.NewClosure(func(transactionId message.Id) {
 		fmt.Println("UNSOLIDIFIABLE:", transactionId)
 	}))
 
-	tangle.Events.TransactionMissing.Attach(events.NewClosure(func(transactionId transaction.Id) {
+	tangle.Events.TransactionMissing.Attach(events.NewClosure(func(transactionId message.Id) {
 		fmt.Println("MISSING:", transactionId)
 	}))
 
-	tangle.Events.TransactionRemoved.Attach(events.NewClosure(func(transactionId transaction.Id) {
+	tangle.Events.TransactionRemoved.Attach(events.NewClosure(func(transactionId message.Id) {
 		fmt.Println("REMOVED:", transactionId)
 	}))
 
-	newTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data")))
-	newTransaction2 := transaction.New(newTransaction1.GetId(), newTransaction1.GetId(), ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some other data")))
+	newTransaction1 := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data")))
+	newTransaction2 := message.New(newTransaction1.GetId(), newTransaction1.GetId(), ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some other data")))
 
 	tangle.AttachTransaction(newTransaction2)
 
diff --git a/packages/binary/tangle/tipselector/events.go b/packages/binary/tangle/tipselector/events.go
index 563e4a7066545d609c13b8b74c9f4a292f3a5607..21f6276eda365408bae0bb38f0b71d7a891fcb0a 100644
--- a/packages/binary/tangle/tipselector/events.go
+++ b/packages/binary/tangle/tipselector/events.go
@@ -1,7 +1,7 @@
 package tipselector
 
 import (
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/hive.go/events"
 )
 
@@ -11,5 +11,5 @@ type Events struct {
 }
 
 func transactionIdEvent(handler interface{}, params ...interface{}) {
-	handler.(func(transaction.Id))(params[0].(transaction.Id))
+	handler.(func(message.Id))(params[0].(message.Id))
 }
diff --git a/packages/binary/tangle/tipselector/tipselector.go b/packages/binary/tangle/tipselector/tipselector.go
index 1b03500cb316ea720589c0bbf2a99fe09b111805..f98d1f50eb3b09e739333c03dc434a084ab36a21 100644
--- a/packages/binary/tangle/tipselector/tipselector.go
+++ b/packages/binary/tangle/tipselector/tipselector.go
@@ -2,7 +2,7 @@ package tipselector
 
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/hive.go/events"
 )
 
@@ -21,7 +21,7 @@ func New() *TipSelector {
 	}
 }
 
-func (tipSelector *TipSelector) AddTip(transaction *transaction.Transaction) {
+func (tipSelector *TipSelector) AddTip(transaction *message.Transaction) {
 	transactionId := transaction.GetId()
 	if tipSelector.tips.Set(transactionId, transactionId) {
 		tipSelector.Events.TipAdded.Trigger(transactionId)
@@ -38,16 +38,16 @@ func (tipSelector *TipSelector) AddTip(transaction *transaction.Transaction) {
 	}
 }
 
-func (tipSelector *TipSelector) GetTips() (trunkTransaction, branchTransaction transaction.Id) {
+func (tipSelector *TipSelector) GetTips() (trunkTransaction, branchTransaction message.Id) {
 	tip := tipSelector.tips.RandomEntry()
 	if tip == nil {
-		trunkTransaction = transaction.EmptyId
-		branchTransaction = transaction.EmptyId
+		trunkTransaction = message.EmptyId
+		branchTransaction = message.EmptyId
 
 		return
 	}
 
-	branchTransaction = tip.(transaction.Id)
+	branchTransaction = tip.(message.Id)
 
 	if tipSelector.tips.Size() == 1 {
 		trunkTransaction = branchTransaction
@@ -55,9 +55,9 @@ func (tipSelector *TipSelector) GetTips() (trunkTransaction, branchTransaction t
 		return
 	}
 
-	trunkTransaction = tipSelector.tips.RandomEntry().(transaction.Id)
+	trunkTransaction = tipSelector.tips.RandomEntry().(message.Id)
 	for trunkTransaction == branchTransaction && tipSelector.tips.Size() > 1 {
-		trunkTransaction = tipSelector.tips.RandomEntry().(transaction.Id)
+		trunkTransaction = tipSelector.tips.RandomEntry().(message.Id)
 	}
 
 	return
diff --git a/packages/binary/tangle/tipselector/tipselector_test.go b/packages/binary/tangle/tipselector/tipselector_test.go
index fe565bd18ec900d3de305abcc849cb91258e3d09..f089f29d993401ab1d3d98b906b7e74e853671f2 100644
--- a/packages/binary/tangle/tipselector/tipselector_test.go
+++ b/packages/binary/tangle/tipselector/tipselector_test.go
@@ -7,8 +7,8 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
 )
 
 func Test(t *testing.T) {
@@ -17,11 +17,11 @@ func Test(t *testing.T) {
 
 	// check if first tips point to genesis
 	trunk1, branch1 := tipSelector.GetTips()
-	assert.Equal(t, transaction.EmptyId, trunk1)
-	assert.Equal(t, transaction.EmptyId, branch1)
+	assert.Equal(t, message.EmptyId, trunk1)
+	assert.Equal(t, message.EmptyId, branch1)
 
 	// create a transaction and attach it
-	transaction1 := transaction.New(trunk1, branch1, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
+	transaction1 := message.New(trunk1, branch1, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction1)
 
 	// check if the tip shows up in the tip count
@@ -33,7 +33,7 @@ func Test(t *testing.T) {
 	assert.Equal(t, transaction1.GetId(), branch2)
 
 	// create a 2nd transaction and attach it
-	transaction2 := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
+	transaction2 := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction2)
 
 	// check if the tip shows up in the tip count
@@ -41,7 +41,7 @@ func Test(t *testing.T) {
 
 	// attach a transaction to our two tips
 	trunk3, branch3 := tipSelector.GetTips()
-	transaction3 := transaction.New(trunk3, branch3, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
+	transaction3 := message.New(trunk3, branch3, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction3)
 
 	// check if the tip shows replaces the current tips
diff --git a/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go b/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go
index 132ca46ece65ecfc6c2bc6ebc6fa50bb64b29171..f6d64f3eb25ae0b828b2195540f5235912111295 100644
--- a/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go
+++ b/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go
@@ -7,14 +7,14 @@ import (
 	"github.com/iotaledger/hive.go/async"
 	"github.com/iotaledger/hive.go/autopeering/peer"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 )
 
 var ErrInvalidSignature = fmt.Errorf("invalid signature")
 
 type TransactionSignatureFilter struct {
-	onAcceptCallback func(tx *transaction.Transaction, peer *peer.Peer)
-	onRejectCallback func(tx *transaction.Transaction, err error, peer *peer.Peer)
+	onAcceptCallback func(tx *message.Transaction, peer *peer.Peer)
+	onRejectCallback func(tx *message.Transaction, err error, peer *peer.Peer)
 	workerPool       async.WorkerPool
 
 	onAcceptCallbackMutex sync.RWMutex
@@ -27,7 +27,7 @@ func NewTransactionSignatureFilter() (result *TransactionSignatureFilter) {
 	return
 }
 
-func (filter *TransactionSignatureFilter) Filter(tx *transaction.Transaction, peer *peer.Peer) {
+func (filter *TransactionSignatureFilter) Filter(tx *message.Transaction, peer *peer.Peer) {
 	filter.workerPool.Submit(func() {
 		if tx.VerifySignature() {
 			filter.getAcceptCallback()(tx, peer)
@@ -37,13 +37,13 @@ func (filter *TransactionSignatureFilter) Filter(tx *transaction.Transaction, pe
 	})
 }
 
-func (filter *TransactionSignatureFilter) OnAccept(callback func(tx *transaction.Transaction, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) OnAccept(callback func(tx *message.Transaction, peer *peer.Peer)) {
 	filter.onAcceptCallbackMutex.Lock()
 	filter.onAcceptCallback = callback
 	filter.onAcceptCallbackMutex.Unlock()
 }
 
-func (filter *TransactionSignatureFilter) OnReject(callback func(tx *transaction.Transaction, err error, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) OnReject(callback func(tx *message.Transaction, err error, peer *peer.Peer)) {
 	filter.onRejectCallbackMutex.Lock()
 	filter.onRejectCallback = callback
 	filter.onRejectCallbackMutex.Unlock()
@@ -53,7 +53,7 @@ func (filter *TransactionSignatureFilter) Shutdown() {
 	filter.workerPool.ShutdownGracefully()
 }
 
-func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *transaction.Transaction, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *message.Transaction, peer *peer.Peer)) {
 	filter.onAcceptCallbackMutex.RLock()
 	result = filter.onAcceptCallback
 	filter.onAcceptCallbackMutex.RUnlock()
@@ -61,7 +61,7 @@ func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *t
 	return
 }
 
-func (filter *TransactionSignatureFilter) getRejectCallback() (result func(tx *transaction.Transaction, err error, peer *peer.Peer)) {
+func (filter *TransactionSignatureFilter) getRejectCallback() (result func(tx *message.Transaction, err error, peer *peer.Peer)) {
 	filter.onRejectCallbackMutex.RLock()
 	result = filter.onRejectCallback
 	filter.onRejectCallbackMutex.RUnlock()
diff --git a/packages/binary/tangle/transactionparser/transaction_filter.go b/packages/binary/tangle/transactionparser/transaction_filter.go
index ac63d23c5bc21fa673bdd4c8dd0062ac7aa23431..c13d94ec4d85c133e7967efd00bad62e19a21faa 100644
--- a/packages/binary/tangle/transactionparser/transaction_filter.go
+++ b/packages/binary/tangle/transactionparser/transaction_filter.go
@@ -3,12 +3,12 @@ package transactionparser
 import (
 	"github.com/iotaledger/hive.go/autopeering/peer"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 )
 
 type TransactionFilter interface {
-	Filter(tx *transaction.Transaction, peer *peer.Peer)
-	OnAccept(callback func(tx *transaction.Transaction, peer *peer.Peer))
-	OnReject(callback func(tx *transaction.Transaction, err error, peer *peer.Peer))
+	Filter(tx *message.Transaction, peer *peer.Peer)
+	OnAccept(callback func(tx *message.Transaction, peer *peer.Peer))
+	OnReject(callback func(tx *message.Transaction, err error, peer *peer.Peer))
 	Shutdown()
 }
diff --git a/packages/binary/tangle/transactionparser/transactionparser.go b/packages/binary/tangle/transactionparser/transactionparser.go
index 8aa1c56d36ea23243587b33fdd33b69480e96885..d684efc3283a5c58a168c11cc0be8dbe1c42fdda 100644
--- a/packages/binary/tangle/transactionparser/transactionparser.go
+++ b/packages/binary/tangle/transactionparser/transactionparser.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/iotaledger/hive.go/autopeering/peer"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser/builtinfilters"
 
 	"github.com/iotaledger/hive.go/events"
@@ -33,10 +33,10 @@ func New() (result *TransactionParser) {
 				handler.(func([]byte, error, *peer.Peer))(params[0].([]byte), params[1].(error), params[2].(*peer.Peer))
 			}),
 			TransactionParsed: events.NewEvent(func(handler interface{}, params ...interface{}) {
-				handler.(func(*transaction.Transaction, *peer.Peer))(params[0].(*transaction.Transaction), params[1].(*peer.Peer))
+				handler.(func(*message.Transaction, *peer.Peer))(params[0].(*message.Transaction), params[1].(*peer.Peer))
 			}),
 			TransactionRejected: events.NewEvent(func(handler interface{}, params ...interface{}) {
-				handler.(func(*transaction.Transaction, error, *peer.Peer))(params[0].(*transaction.Transaction), params[1].(error), params[2].(*peer.Peer))
+				handler.(func(*message.Transaction, error, *peer.Peer))(params[0].(*message.Transaction), params[1].(error), params[2].(*peer.Peer))
 			}),
 		},
 	}
@@ -121,13 +121,13 @@ func (transactionParser *TransactionParser) setupTransactionsFilterDataFlow() {
 		numberOfTransactionFilters := len(transactionParser.transactionFilters)
 		for i := 0; i < numberOfTransactionFilters; i++ {
 			if i == numberOfTransactionFilters-1 {
-				transactionParser.transactionFilters[i].OnAccept(func(tx *transaction.Transaction, peer *peer.Peer) {
+				transactionParser.transactionFilters[i].OnAccept(func(tx *message.Transaction, peer *peer.Peer) {
 					transactionParser.Events.TransactionParsed.Trigger(tx, peer)
 				})
 			} else {
 				transactionParser.transactionFilters[i].OnAccept(transactionParser.transactionFilters[i+1].Filter)
 			}
-			transactionParser.transactionFilters[i].OnReject(func(tx *transaction.Transaction, err error, peer *peer.Peer) {
+			transactionParser.transactionFilters[i].OnReject(func(tx *message.Transaction, err error, peer *peer.Peer) {
 				transactionParser.Events.TransactionRejected.Trigger(tx, err, peer)
 			})
 		}
@@ -136,7 +136,7 @@ func (transactionParser *TransactionParser) setupTransactionsFilterDataFlow() {
 }
 
 func (transactionParser *TransactionParser) parseTransaction(bytes []byte, peer *peer.Peer) {
-	if parsedTransaction, err, _ := transaction.FromBytes(bytes); err != nil {
+	if parsedTransaction, err, _ := message.FromBytes(bytes); err != nil {
 		transactionParser.Events.BytesRejected.Trigger(bytes, err, peer)
 	} else {
 		transactionParser.transactionFilters[0].Filter(parsedTransaction, peer)
diff --git a/packages/binary/tangle/transactionparser/transactionparser_test.go b/packages/binary/tangle/transactionparser/transactionparser_test.go
index 85309414cc9643750348e7657098e22e29bc88d2..9a5f64138ab321ef06f4c9844e1f137d0b0d6bc0 100644
--- a/packages/binary/tangle/transactionparser/transactionparser_test.go
+++ b/packages/binary/tangle/transactionparser/transactionparser_test.go
@@ -9,12 +9,12 @@ import (
 	"github.com/iotaledger/hive.go/events"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data"
 )
 
 func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) {
-	txBytes := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"))).Bytes()
+	txBytes := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"))).Bytes()
 	txParser := New()
 
 	b.ResetTimer()
@@ -29,7 +29,7 @@ func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) {
 func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) {
 	transactionBytes := make([][]byte, b.N)
 	for i := 0; i < b.N; i++ {
-		transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"+strconv.Itoa(i)))).Bytes()
+		transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"+strconv.Itoa(i)))).Bytes()
 	}
 
 	txParser := New()
@@ -44,12 +44,12 @@ func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) {
 }
 
 func TestTransactionParser_ParseTransaction(t *testing.T) {
-	tx := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test")))
+	tx := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test")))
 
 	txParser := New()
 	txParser.Parse(tx.Bytes(), nil)
 
-	txParser.Events.TransactionParsed.Attach(events.NewClosure(func(tx *transaction.Transaction) {
+	txParser.Events.TransactionParsed.Attach(events.NewClosure(func(tx *message.Transaction) {
 		fmt.Println("PARSED!!!")
 	}))
 
diff --git a/packages/binary/tangle/transactionrequester/transactionrequester.go b/packages/binary/tangle/transactionrequester/transactionrequester.go
index 6f036e810abd8f17844ec0bd5f1ffb8c06a3c024..2016298c71b42658d3b832c2040285118de61175 100644
--- a/packages/binary/tangle/transactionrequester/transactionrequester.go
+++ b/packages/binary/tangle/transactionrequester/transactionrequester.go
@@ -7,11 +7,11 @@ import (
 	"github.com/iotaledger/hive.go/async"
 	"github.com/iotaledger/hive.go/events"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 )
 
 type TransactionRequester struct {
-	scheduledRequests map[transaction.Id]*time.Timer
+	scheduledRequests map[message.Id]*time.Timer
 	requestWorker     async.NonBlockingWorkerPool
 	options           *Options
 	Events            Events
@@ -21,11 +21,11 @@ type TransactionRequester struct {
 
 func New(optionalOptions ...Option) *TransactionRequester {
 	requester := &TransactionRequester{
-		scheduledRequests: make(map[transaction.Id]*time.Timer),
+		scheduledRequests: make(map[message.Id]*time.Timer),
 		options:           newOptions(optionalOptions),
 		Events: Events{
 			SendRequest: events.NewEvent(func(handler interface{}, params ...interface{}) {
-				handler.(func(transaction.Id))(params[0].(transaction.Id))
+				handler.(func(message.Id))(params[0].(message.Id))
 			}),
 		},
 	}
@@ -35,7 +35,7 @@ func New(optionalOptions ...Option) *TransactionRequester {
 	return requester
 }
 
-func (requester *TransactionRequester) ScheduleRequest(transactionId transaction.Id) {
+func (requester *TransactionRequester) ScheduleRequest(transactionId message.Id) {
 	var retryRequest func(bool)
 	retryRequest = func(initialRequest bool) {
 		requester.requestWorker.Submit(func() {
@@ -58,7 +58,7 @@ func (requester *TransactionRequester) ScheduleRequest(transactionId transaction
 	retryRequest(true)
 }
 
-func (requester *TransactionRequester) StopRequest(transactionId transaction.Id) {
+func (requester *TransactionRequester) StopRequest(transactionId message.Id) {
 	requester.scheduledRequestsMutex.RLock()
 	if timer, timerExists := requester.scheduledRequests[transactionId]; timerExists {
 		requester.scheduledRequestsMutex.RUnlock()
diff --git a/packages/binary/valuetransfer/address/address.go b/packages/binary/valuetransfer/address/address.go
index 13e89793906ed7ee166a48977824b312c1a5a00c..265fbf0cd394b153480ac557fc1c99bc8d310803 100644
--- a/packages/binary/valuetransfer/address/address.go
+++ b/packages/binary/valuetransfer/address/address.go
@@ -11,13 +11,13 @@ import (
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 )
 
-type AddressVersion = byte
+type Version = byte
 
-type AddressDigest = []byte
+type Digest = []byte
 
 type Address [Length]byte
 
-// Random create a random address, which can for example be used in unit tests.
+// Random creates a random address, which can for example be used in unit tests.
 func Random() (address Address) {
 	// generate a random sequence of bytes
 	addressBytes := make([]byte, Length)
@@ -31,7 +31,7 @@ func Random() (address Address) {
 	return
 }
 
-// FromBase58 creates an address from base58 encoded string.
+// FromBase58 creates an address from a base58 encoded string.
 func FromBase58(base58String string) (address Address, err error) {
 	// decode string
 	bytes, err := base58.Decode(base58String)
@@ -52,6 +52,7 @@ func FromBase58(base58String string) (address Address, err error) {
 	return
 }
 
+// FromED25519PubKey creates an address from an ed25519 public key.
 func FromED25519PubKey(key ed25119.PublicKey) (address Address) {
 	digest := blake2b.Sum256(key[:])
 
@@ -75,7 +76,7 @@ func FromBytes(bytes []byte) (result Address, err error, consumedBytes int) {
 	return
 }
 
-// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+// Parse is a wrapper for simplified unmarshaling of a byte stream using the marshalUtil package.
 func Parse(marshalUtil *marshalutil.MarshalUtil) (Address, error) {
 	if address, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
 		return Address{}, err
@@ -84,11 +85,13 @@ func Parse(marshalUtil *marshalutil.MarshalUtil) (Address, error) {
 	}
 }
 
-func (address *Address) GetVersion() AddressVersion {
+// Version returns the version of the address, which corresponds to the signature scheme that is used.
+func (address *Address) Version() Version {
 	return address[0]
 }
 
-func (address *Address) GetDigest() AddressDigest {
+// Digest returns the digest part of an address (i.e. the hashed version of the ed25519 public key)-
+func (address *Address) Digest() Digest {
 	return address[1:]
 }
 
@@ -102,4 +105,5 @@ func (address Address) String() string {
 	return base58.Encode(address.Bytes())
 }
 
+// Length contains the length of an address (digest length = 32 + version byte length = 1).
 const Length = 33
diff --git a/packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go b/packages/binary/valuetransfer/address/signaturescheme/ed25519.go
similarity index 95%
rename from packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go
rename to packages/binary/valuetransfer/address/signaturescheme/ed25519.go
index e7423f5512ee7f10ad5e889a19cccafde6246cf5..997de93a6391b5dbb511091c0d69b7960d8d8fe3 100644
--- a/packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go
+++ b/packages/binary/valuetransfer/address/signaturescheme/ed25519.go
@@ -1,4 +1,4 @@
-package signatures
+package signaturescheme
 
 import (
 	"fmt"
@@ -64,7 +64,7 @@ type ed25519Signature struct {
 
 // ed25519SignatureFromBytes unmarshals an ed25519 signatures from a sequence of bytes.
 // It either creates a new signature or fills the optionally provided object with the parsed information.
-func ed25519SignatureFromBytes(bytes []byte, optionalTargetObject ...*ed25519Signature) (result *ed25519Signature, err error, consumedBytes int) {
+func Ed25519SignatureFromBytes(bytes []byte, optionalTargetObject ...*ed25519Signature) (result *ed25519Signature, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -128,7 +128,4 @@ func (signature *ed25519Signature) Address() address.Address {
 	return address.FromED25519PubKey(signature.publicKey)
 }
 
-// interface contract (allow the compiler to check if the implementation has all of the required methods).
-var _ Signature = &ed25519Signature{}
-
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/valuetransfer/address/signaturescheme/signature.go b/packages/binary/valuetransfer/address/signaturescheme/signature.go
new file mode 100644
index 0000000000000000000000000000000000000000..1e89e733e0935f6ff2b7a10c717f26ba81ba837a
--- /dev/null
+++ b/packages/binary/valuetransfer/address/signaturescheme/signature.go
@@ -0,0 +1,15 @@
+package signaturescheme
+
+import "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+
+// Signature defines an interface for an address signature generated by the corresponding signature scheme.
+type Signature interface {
+	// IsValid returns true if the signature is valid for the given data.
+	IsValid(signedData []byte) bool
+
+	// Bytes returns a marshaled version of the signature.
+	Bytes() []byte
+
+	// Address returns the address, that this signature signs.
+	Address() address.Address
+}
diff --git a/packages/binary/valuetransfer/address/signaturescheme/signaturescheme.go b/packages/binary/valuetransfer/address/signaturescheme/signaturescheme.go
new file mode 100644
index 0000000000000000000000000000000000000000..2abe2f6add2bb877994281684f1daeec2239e7ae
--- /dev/null
+++ b/packages/binary/valuetransfer/address/signaturescheme/signaturescheme.go
@@ -0,0 +1,17 @@
+package signaturescheme
+
+import (
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+)
+
+// SignatureScheme defines an interface for different signature generation methods (i.e. ED25519, WOTS, and so on ...).
+type SignatureScheme interface {
+	// Version returns the version byte that is associated to this signature scheme.
+	Version() byte
+
+	// Address returns the address that this signature scheme instance is securing.
+	Address() address.Address
+
+	// Sign creates a valid signature for the given data according to the signature scheme implementation.
+	Sign(data []byte) Signature
+}
diff --git a/packages/binary/valuetransfer/balance/balance.go b/packages/binary/valuetransfer/balance/balance.go
new file mode 100644
index 0000000000000000000000000000000000000000..e9e3acb2747efa07138ec022c85747c98d64c6fa
--- /dev/null
+++ b/packages/binary/valuetransfer/balance/balance.go
@@ -0,0 +1,79 @@
+package balance
+
+import (
+	"strconv"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+)
+
+type Balance struct {
+	value int64
+	color Color
+}
+
+func New(color Color, balance int64) (result *Balance) {
+	result = &Balance{
+		color: color,
+		value: balance,
+	}
+
+	return
+}
+
+func FromBytes(bytes []byte) (result *Balance, err error, consumedBytes int) {
+	result = &Balance{}
+
+	marshalUtil := marshalutil.New(bytes)
+
+	result.value, err = marshalUtil.ReadInt64()
+	if err != nil {
+		return
+	}
+
+	if coinColor, colorErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) {
+		return ColorFromBytes(data)
+	}); colorErr != nil {
+		return nil, colorErr, marshalUtil.ReadOffset()
+	} else {
+		result.color = coinColor.(Color)
+	}
+
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+func Parse(marshalUtil *marshalutil.MarshalUtil) (*Balance, error) {
+	if address, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
+		return nil, err
+	} else {
+		return address.(*Balance), nil
+	}
+}
+
+// Value returns the numeric value of the balance.
+func (balance *Balance) Value() int64 {
+	return balance.value
+}
+
+// Color returns the Color of the balance.
+func (balance *Balance) Color() Color {
+	return balance.color
+}
+
+func (balance *Balance) Bytes() []byte {
+	marshalUtil := marshalutil.New(Length)
+
+	marshalUtil.WriteInt64(balance.value)
+	marshalUtil.WriteBytes(balance.color.Bytes())
+
+	return marshalUtil.Bytes()
+}
+
+func (balance *Balance) String() string {
+	return strconv.FormatInt(balance.value, 10) + " " + balance.color.String()
+}
+
+// Length encodes the length of a marshaled Balance (the length of the color + 8 bytes for the balance).
+const Length = 8 + ColorLength
diff --git a/packages/binary/valuetransfer/balance/balance_test.go b/packages/binary/valuetransfer/balance/balance_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..98fbd6bb574fb9362cb69a86c2d3f97689548936
--- /dev/null
+++ b/packages/binary/valuetransfer/balance/balance_test.go
@@ -0,0 +1,24 @@
+package balance
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMarshalUnmarshal(t *testing.T) {
+	balance := New(COLOR_IOTA, 1337)
+	assert.Equal(t, int64(1337), balance.Value())
+	assert.Equal(t, COLOR_IOTA, balance.Color())
+
+	marshaledBalance := balance.Bytes()
+	assert.Equal(t, Length, len(marshaledBalance))
+
+	restoredBalance, err, consumedBytes := FromBytes(marshaledBalance)
+	if err != nil {
+		panic(err)
+	}
+	assert.Equal(t, Length, consumedBytes)
+	assert.Equal(t, balance.value, restoredBalance.Value())
+	assert.Equal(t, balance.color, restoredBalance.Color())
+}
diff --git a/packages/binary/valuetransfer/coloredbalance/color/color.go b/packages/binary/valuetransfer/balance/color.go
similarity index 51%
rename from packages/binary/valuetransfer/coloredbalance/color/color.go
rename to packages/binary/valuetransfer/balance/color.go
index 5afcc6915849ad52e53c4b31a814797225b935a0..a37941884dfc6b14402df0112d698715a4f7dde0 100644
--- a/packages/binary/valuetransfer/coloredbalance/color/color.go
+++ b/packages/binary/valuetransfer/balance/color.go
@@ -1,4 +1,4 @@
-package color
+package balance
 
 import (
 	"github.com/mr-tron/base58"
@@ -6,32 +6,32 @@ import (
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
-type Color [Length]byte
+type Color [ColorLength]byte
 
-func FromBytes(bytes []byte) (result Color, err error, consumedBytes int) {
-	colorBytes, err := marshalutil.New(bytes).ReadBytes(Length)
+func ColorFromBytes(bytes []byte) (result Color, err error, consumedBytes int) {
+	colorBytes, err := marshalutil.New(bytes).ReadBytes(ColorLength)
 	if err != nil {
 		return
 	}
 	copy(result[:], colorBytes)
 
-	consumedBytes = Length
+	consumedBytes = ColorLength
 
 	return
 }
 
-const Length = 32
+const ColorLength = 32
 
 func (color Color) Bytes() []byte {
 	return color[:]
 }
 
 func (color Color) String() string {
-	if color == IOTA {
+	if color == COLOR_IOTA {
 		return "IOTA"
 	}
 
 	return base58.Encode(color[:])
 }
 
-var IOTA Color = [32]byte{}
+var COLOR_IOTA Color = [32]byte{}
diff --git a/packages/binary/valuetransfer/coloredbalance/coloredbalance.go b/packages/binary/valuetransfer/coloredbalance/coloredbalance.go
deleted file mode 100644
index e17e8d68aae98a752aca9d82f1303ccb185b5419..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfer/coloredbalance/coloredbalance.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package coloredbalance
-
-import (
-	"strconv"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance/color"
-)
-
-type ColoredBalance struct {
-	color   color.Color
-	balance int64
-}
-
-func New(color color.Color, balance int64) (result *ColoredBalance) {
-	result = &ColoredBalance{
-		color:   color,
-		balance: balance,
-	}
-
-	return
-}
-
-func FromBytes(bytes []byte) (result *ColoredBalance, err error, consumedBytes int) {
-	result = &ColoredBalance{}
-
-	marshalUtil := marshalutil.New(bytes)
-
-	if coinColor, colorErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) {
-		return color.FromBytes(data)
-	}); colorErr != nil {
-		return nil, colorErr, marshalUtil.ReadOffset()
-	} else {
-		result.color = coinColor.(color.Color)
-	}
-
-	result.balance, err = marshalUtil.ReadInt64()
-	if err != nil {
-		return
-	}
-
-	consumedBytes = marshalUtil.ReadOffset()
-
-	return
-}
-
-// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
-func Parse(marshalUtil *marshalutil.MarshalUtil) (*ColoredBalance, error) {
-	if address, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
-		return nil, err
-	} else {
-		return address.(*ColoredBalance), nil
-	}
-}
-
-func (balance *ColoredBalance) Bytes() []byte {
-	marshalUtil := marshalutil.New(color.Length + marshalutil.UINT32_SIZE)
-
-	marshalUtil.WriteBytes(balance.color.Bytes())
-	marshalUtil.WriteInt64(balance.balance)
-
-	return marshalUtil.Bytes()
-}
-
-func (balance *ColoredBalance) String() string {
-	return strconv.FormatInt(balance.balance, 10) + " " + balance.color.String()
-}
-
-// Length encodes the length of a marshaled ColoredBalance (the length of the color + 8 bytes for the balance).
-const Length = color.Length + 8
diff --git a/packages/binary/valuetransfer/payload/cached_object.go b/packages/binary/valuetransfer/payload/cached_object.go
deleted file mode 100644
index 5c1639b1685cf5627fe8164f14c12922bc34f89c..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfer/payload/cached_object.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package payload
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-// CachedObject is a wrapper for the object storage, that takes care of type casting the managed objects.
-// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
-// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
-// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
-// specialized types of CachedObjects, without having to manually type cast over and over again.
-type CachedObject struct {
-	objectstorage.CachedObject
-}
-
-// Retain wraps the underlying method to return a new "wrapped object".
-func (cachedPayload *CachedObject) Retain() *CachedObject {
-	return &CachedObject{cachedPayload.CachedObject.Retain()}
-}
-
-// Consume wraps the underlying method to return the correctly typed objects in the callback.
-func (cachedPayload *CachedObject) Consume(consumer func(payload *Payload)) bool {
-	return cachedPayload.CachedObject.Consume(func(object objectstorage.StorableObject) {
-		consumer(object.(*Payload))
-	})
-}
-
-// Unwrap provides a way to "Get" a type casted version of the underlying object.
-func (cachedPayload *CachedObject) Unwrap() *Payload {
-	if untypedTransaction := cachedPayload.Get(); untypedTransaction == nil {
-		return nil
-	} else {
-		if typeCastedTransaction := untypedTransaction.(*Payload); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
-			return nil
-		} else {
-			return typeCastedTransaction
-		}
-	}
-}
diff --git a/packages/binary/valuetransfer/payload/id/id.go b/packages/binary/valuetransfer/payload/id.go
similarity index 63%
rename from packages/binary/valuetransfer/payload/id/id.go
rename to packages/binary/valuetransfer/payload/id.go
index 8a8f24f0425dff74cef572d337cbf443391fcd3b..d35da66160d2818026caecc01881b069cae30cfe 100644
--- a/packages/binary/valuetransfer/payload/id/id.go
+++ b/packages/binary/valuetransfer/payload/id.go
@@ -1,4 +1,4 @@
-package id
+package payload
 
 import (
 	"fmt"
@@ -9,16 +9,16 @@ import (
 )
 
 // Id represents the hash of a payload that is used to identify the given payload.
-type Id [Length]byte
+type Id [IdLength]byte
 
-// New creates a payload id from a base58 encoded string.
-func New(base58EncodedString string) (result Id, err error) {
+// NewId creates a payload id from a base58 encoded string.
+func NewId(base58EncodedString string) (result Id, err error) {
 	bytes, err := base58.Decode(base58EncodedString)
 	if err != nil {
 		return
 	}
 
-	if len(bytes) != Length {
+	if len(bytes) != IdLength {
 		err = fmt.Errorf("length of base58 formatted payload id is wrong")
 
 		return
@@ -29,18 +29,18 @@ func New(base58EncodedString string) (result Id, err error) {
 	return
 }
 
-// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
-func Parse(marshalUtil *marshalutil.MarshalUtil) (Id, error) {
-	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
+// ParseId is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+func ParseId(marshalUtil *marshalutil.MarshalUtil) (Id, error) {
+	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) }); err != nil {
 		return Id{}, err
 	} else {
 		return id.(Id), nil
 	}
 }
 
-// FromBytes unmarshals a payload id from a sequence of bytes.
+// IdFromBytes unmarshals a payload id from a sequence of bytes.
 // It either creates a new payload id or fills the optionally provided object with the parsed information.
-func FromBytes(bytes []byte, optionalTargetObject ...*Id) (result Id, err error, consumedBytes int) {
+func IdFromBytes(bytes []byte, optionalTargetObject ...*Id) (result Id, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	var targetObject *Id
 	switch len(optionalTargetObject) {
@@ -49,14 +49,14 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Id) (result Id, err error,
 	case 1:
 		targetObject = optionalTargetObject[0]
 	default:
-		panic("too many arguments in call to FromBytes")
+		panic("too many arguments in call to IdFromBytes")
 	}
 
 	// initialize helper
 	marshalUtil := marshalutil.New(bytes)
 
 	// read id from bytes
-	idBytes, err := marshalUtil.ReadBytes(Length)
+	idBytes, err := marshalUtil.ReadBytes(IdLength)
 	if err != nil {
 		return
 	}
@@ -81,7 +81,7 @@ func (id Id) Bytes() []byte {
 }
 
 // Empty represents the id encoding the genesis.
-var Genesis Id
+var GenesisId Id
 
-// Length defined the amount of bytes in a payload id (32 bytes hash value).
-const Length = 32
+// IdLength defined the amount of bytes in a payload id (32 bytes hash value).
+const IdLength = 32
diff --git a/packages/binary/valuetransfer/payload/id/id_test.go b/packages/binary/valuetransfer/payload/id_test.go
similarity index 70%
rename from packages/binary/valuetransfer/payload/id/id_test.go
rename to packages/binary/valuetransfer/payload/id_test.go
index 86fd25ab18cd9c0a41dc8d8c3120fdca8939b4e2..22ff3b66bd5c0cdafb153e354453473c52d5281b 100644
--- a/packages/binary/valuetransfer/payload/id/id_test.go
+++ b/packages/binary/valuetransfer/payload/id_test.go
@@ -1,4 +1,4 @@
-package id
+package payload
 
 import (
 	"testing"
@@ -8,14 +8,14 @@ import (
 
 func Test(t *testing.T) {
 	// create variable for id
-	sourceId, err := New("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM")
+	sourceId, err := NewId("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM")
 	if err != nil {
 		panic(err)
 	}
 
 	// read serialized id into both variables
 	var restoredIdPointer Id
-	restoredIdValue, err, _ := FromBytes(sourceId.Bytes(), &restoredIdPointer)
+	restoredIdValue, err, _ := IdFromBytes(sourceId.Bytes(), &restoredIdPointer)
 	if err != nil {
 		panic(err)
 	}
diff --git a/packages/binary/valuetransfer/payload/payload.go b/packages/binary/valuetransfer/payload/payload.go
index ee248f7dbb20167cc783f4cab6c8072bda963743..c97b2f128cc97537eedb61568b56f4a4ce1e3596 100644
--- a/packages/binary/valuetransfer/payload/payload.go
+++ b/packages/binary/valuetransfer/payload/payload.go
@@ -8,36 +8,34 @@ import (
 	"golang.org/x/crypto/blake2b"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 )
 
 type Payload struct {
 	objectstorage.StorableObjectFlags
 
-	trunkPayloadId  payloadid.Id
-	branchPayloadId payloadid.Id
-	transfer        *transfer.Transfer
+	trunkPayloadId  Id
+	branchPayloadId Id
+	transaction     *transaction.Transaction
 
-	id      *payloadid.Id
+	id      *Id
 	idMutex sync.RWMutex
 
 	bytes      []byte
 	bytesMutex sync.RWMutex
 }
 
-func New(trunkPayloadId, branchPayloadId payloadid.Id, valueTransfer *transfer.Transfer) *Payload {
+func New(trunkPayloadId, branchPayloadId Id, valueTransfer *transaction.Transaction) *Payload {
 	return &Payload{
 		trunkPayloadId:  trunkPayloadId,
 		branchPayloadId: branchPayloadId,
-		transfer:        valueTransfer,
+		transaction:     valueTransfer,
 	}
 }
 
 func FromStorage(key []byte) objectstorage.StorableObject {
-	id, err, _ := payloadid.FromBytes(key)
+	id, err, _ := IdFromBytes(key)
 	if err != nil {
 		panic(err)
 	}
@@ -74,25 +72,25 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload,
 	}
 
 	// parse trunk payload id
-	parsedTrunkPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return payloadid.FromBytes(data) })
+	parsedTrunkPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.trunkPayloadId = parsedTrunkPayloadId.(payloadid.Id)
+	result.trunkPayloadId = parsedTrunkPayloadId.(Id)
 
 	// parse branch payload id
-	parsedBranchPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return payloadid.FromBytes(data) })
+	parsedBranchPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.branchPayloadId = parsedBranchPayloadId.(payloadid.Id)
+	result.branchPayloadId = parsedBranchPayloadId.(Id)
 
 	// parse transfer
-	parsedTransfer, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return transfer.FromBytes(data) })
+	parsedTransfer, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return transaction.FromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.transfer = parsedTransfer.(*transfer.Transfer)
+	result.transaction = parsedTransfer.(*transaction.Transaction)
 
 	// return the number of bytes we processed
 	consumedBytes = marshalUtil.ReadOffset()
@@ -103,7 +101,7 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload,
 	return
 }
 
-func (payload *Payload) GetId() payloadid.Id {
+func (payload *Payload) Id() Id {
 	// acquire lock for reading id
 	payload.idMutex.RLock()
 
@@ -125,27 +123,27 @@ func (payload *Payload) GetId() payloadid.Id {
 	}
 
 	// otherwise calculate the id
-	marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length + transferid.Length)
+	marshalUtil := marshalutil.New(IdLength + IdLength + transaction.IdLength)
 	marshalUtil.WriteBytes(payload.trunkPayloadId.Bytes())
 	marshalUtil.WriteBytes(payload.branchPayloadId.Bytes())
-	marshalUtil.WriteBytes(payload.GetTransfer().GetId().Bytes())
+	marshalUtil.WriteBytes(payload.Transaction().Id().Bytes())
 
-	var id payloadid.Id = blake2b.Sum256(marshalUtil.Bytes())
+	var id Id = blake2b.Sum256(marshalUtil.Bytes())
 	payload.id = &id
 
 	return id
 }
 
-func (payload *Payload) GetTrunkId() payloadid.Id {
+func (payload *Payload) TrunkId() Id {
 	return payload.trunkPayloadId
 }
 
-func (payload *Payload) GetBranchId() payloadid.Id {
+func (payload *Payload) BranchId() Id {
 	return payload.branchPayloadId
 }
 
-func (payload *Payload) GetTransfer() *transfer.Transfer {
-	return payload.transfer
+func (payload *Payload) Transaction() *transaction.Transaction {
+	return payload.transaction
 }
 
 func (payload *Payload) Bytes() (bytes []byte) {
@@ -170,13 +168,13 @@ func (payload *Payload) Bytes() (bytes []byte) {
 	}
 
 	// retrieve bytes of transfer
-	transferBytes, err := payload.GetTransfer().MarshalBinary()
+	transferBytes, err := payload.Transaction().MarshalBinary()
 	if err != nil {
 		return
 	}
 
 	// marshal fields
-	payloadLength := payloadid.Length + payloadid.Length + len(transferBytes)
+	payloadLength := IdLength + IdLength + len(transferBytes)
 	marshalUtil := marshalutil.New(marshalutil.UINT32_SIZE + marshalutil.UINT32_SIZE + payloadLength)
 	marshalUtil.WriteUint32(Type)
 	marshalUtil.WriteUint32(uint32(payloadLength))
@@ -193,10 +191,10 @@ func (payload *Payload) Bytes() (bytes []byte) {
 
 func (payload *Payload) String() string {
 	return stringify.Struct("Payload",
-		stringify.StructField("id", payload.GetId()),
-		stringify.StructField("trunk", payload.GetTrunkId()),
-		stringify.StructField("branch", payload.GetBranchId()),
-		stringify.StructField("transfer", payload.GetTransfer()),
+		stringify.StructField("id", payload.Id()),
+		stringify.StructField("trunk", payload.TrunkId()),
+		stringify.StructField("branch", payload.BranchId()),
+		stringify.StructField("transfer", payload.Transaction()),
 	)
 }
 
@@ -239,7 +237,7 @@ var _ payload.Payload = &Payload{}
 // UnmarshalBinary(data []byte) (err error) already implemented by Payload
 
 func (payload *Payload) GetStorageKey() []byte {
-	id := payload.GetId()
+	id := payload.Id()
 
 	return id[:]
 }
@@ -252,3 +250,37 @@ func (payload *Payload) Update(other objectstorage.StorableObject) {
 var _ objectstorage.StorableObject = &Payload{}
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CachedPayload is a wrapper for the object storage, that takes care of type casting the managed objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of CachedObjects, without having to manually type cast over and over again.
+type CachedPayload struct {
+	objectstorage.CachedObject
+}
+
+// Retain wraps the underlying method to return a new "wrapped object".
+func (cachedPayload *CachedPayload) Retain() *CachedPayload {
+	return &CachedPayload{cachedPayload.CachedObject.Retain()}
+}
+
+// Consume wraps the underlying method to return the correctly typed objects in the callback.
+func (cachedPayload *CachedPayload) Consume(consumer func(payload *Payload)) bool {
+	return cachedPayload.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*Payload))
+	})
+}
+
+// Unwrap provides a way to "Get" a type casted version of the underlying object.
+func (cachedPayload *CachedPayload) Unwrap() *Payload {
+	if untypedTransaction := cachedPayload.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*Payload); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/payload/payload_test.go b/packages/binary/valuetransfer/payload/payload_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0dc219378c233b2cc8daeec57eebca4e7fa313b8
--- /dev/null
+++ b/packages/binary/valuetransfer/payload/payload_test.go
@@ -0,0 +1,120 @@
+package payload
+
+import (
+	"fmt"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
+)
+
+func ExamplePayload() {
+	// 1. create value transfer (user provides this)
+	valueTransfer := transaction.New(
+		// inputs
+		transaction.NewInputs(
+			transaction.NewOutputId(address.Random(), transaction.RandomId()),
+			transaction.NewOutputId(address.Random(), transaction.RandomId()),
+		),
+
+		// outputs
+		transaction.NewOutputs(map[address.Address][]*balance.Balance{
+			address.Random(): {
+				balance.New(balance.COLOR_IOTA, 1337),
+			},
+		}),
+	)
+
+	// 2. create value payload (the ontology creates this and wraps the user provided transfer accordingly)
+	valuePayload := New(
+		// trunk in "value transfer ontology" (filled by ontology tipSelector)
+		GenesisId,
+
+		// branch in "value transfer ontology"  (filled by ontology tipSelector)
+		GenesisId,
+
+		// value transfer
+		valueTransfer,
+	)
+
+	// 3. build actual transaction (the base layer creates this and wraps the ontology provided payload)
+	tx := message.New(
+		// trunk in "network tangle" ontology (filled by tipSelector)
+		message.EmptyId,
+
+		// branch in "network tangle" ontology (filled by tipSelector)
+		message.EmptyId,
+
+		// issuer of the transaction (signs automatically)
+		ed25119.GenerateKeyPair(),
+
+		// the time when the transaction was created
+		time.Now(),
+
+		// the ever increasing sequence number of this transaction
+		0,
+
+		// payload
+		valuePayload,
+	)
+
+	fmt.Println(tx)
+}
+
+func TestPayload(t *testing.T) {
+	addressKeyPair1 := ed25119.GenerateKeyPair()
+	addressKeyPair2 := ed25119.GenerateKeyPair()
+
+	originalPayload := New(
+		GenesisId,
+		GenesisId,
+		transaction.New(
+			transaction.NewInputs(
+				transaction.NewOutputId(address.FromED25519PubKey(addressKeyPair1.PublicKey), transaction.RandomId()),
+				transaction.NewOutputId(address.FromED25519PubKey(addressKeyPair2.PublicKey), transaction.RandomId()),
+			),
+
+			transaction.NewOutputs(map[address.Address][]*balance.Balance{
+				address.Random(): {
+					balance.New(balance.COLOR_IOTA, 1337),
+				},
+			}),
+		).Sign(
+			signaturescheme.ED25519(addressKeyPair1),
+		),
+	)
+
+	assert.Equal(t, false, originalPayload.Transaction().SignaturesValid())
+
+	originalPayload.Transaction().Sign(
+		signaturescheme.ED25519(addressKeyPair2),
+	)
+
+	assert.Equal(t, true, originalPayload.Transaction().SignaturesValid())
+
+	clonedPayload1, err, _ := FromBytes(originalPayload.Bytes())
+	if err != nil {
+		panic(err)
+	}
+
+	assert.Equal(t, originalPayload.BranchId(), clonedPayload1.BranchId())
+	assert.Equal(t, originalPayload.TrunkId(), clonedPayload1.TrunkId())
+	assert.Equal(t, originalPayload.Transaction().Bytes(), clonedPayload1.Transaction().Bytes())
+	assert.Equal(t, originalPayload.Id(), clonedPayload1.Id())
+	assert.Equal(t, true, clonedPayload1.Transaction().SignaturesValid())
+
+	clonedPayload2, err, _ := FromBytes(clonedPayload1.Bytes())
+	if err != nil {
+		panic(err)
+	}
+
+	assert.Equal(t, originalPayload.Id(), clonedPayload2.Id())
+	assert.Equal(t, true, clonedPayload2.Transaction().SignaturesValid())
+}
diff --git a/packages/binary/valuetransfer/payload/transfer/id/id.go b/packages/binary/valuetransfer/payload/transfer/id/id.go
deleted file mode 100644
index 22baa614a721dd92c77efb4720cd54821889348a..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfer/payload/transfer/id/id.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package id
-
-import (
-	"github.com/mr-tron/base58"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-)
-
-type Id [Length]byte
-
-func New(idBytes []byte) (result Id) {
-	copy(result[:], idBytes)
-
-	return
-}
-
-// FromBytes unmarshals a transfer id from a sequence of bytes.
-func FromBytes(bytes []byte) (result Id, err error, consumedBytes int) {
-	// parse the bytes
-	marshalUtil := marshalutil.New(bytes)
-	idBytes, err := marshalUtil.ReadBytes(Length)
-	if err != nil {
-		return
-	}
-	copy(result[:], idBytes)
-	consumedBytes = marshalUtil.ReadOffset()
-
-	return
-}
-
-// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
-func Parse(marshalUtil *marshalutil.MarshalUtil) (Id, error) {
-	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
-		return Id{}, err
-	} else {
-		return id.(Id), nil
-	}
-}
-
-func (id Id) Bytes() []byte {
-	return id[:]
-}
-
-func (id Id) String() string {
-	return base58.Encode(id[:])
-}
-
-const Length = 32
diff --git a/packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go b/packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go
deleted file mode 100644
index 81fe7247e3355d5154eae174f1ba70a948e58350..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package signatures
-
-import "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-
-// SignatureScheme defines an interface for different signature generation methods (i.e. ED25519, WOTS, and so on ...).
-type SignatureScheme interface {
-	// Version returns the version byte that is associated to this signature scheme.
-	Version() byte
-
-	// Address returns the address that this signature scheme instance is securing.
-	Address() address.Address
-
-	// Sign creates a valid signature for the given data according to the signature scheme implementation.
-	Sign(data []byte) Signature
-}
-
-// Signature defines an interface for an address signature generated by the corresponding signature scheme.
-type Signature interface {
-	// IsValid returns true if the signature is valid for the given data.
-	IsValid(signedData []byte) bool
-
-	// Bytes returns a marshaled version of the signature.
-	Bytes() []byte
-
-	// Address returns the address, that this signature signs.
-	Address() address.Address
-}
diff --git a/packages/binary/valuetransfer/payload/transfer/transfer.go b/packages/binary/valuetransfer/payload/transfer/transfer.go
deleted file mode 100644
index 0703fcba9f5987ce14dd917f228155e9c4b95ad9..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfer/payload/transfer/transfer.go
+++ /dev/null
@@ -1,296 +0,0 @@
-package transfer
-
-import (
-	"fmt"
-	"sync"
-
-	"github.com/iotaledger/hive.go/objectstorage"
-	"github.com/iotaledger/hive.go/stringify"
-	"github.com/mr-tron/base58"
-	"golang.org/x/crypto/blake2b"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/inputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/outputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/signatures"
-)
-
-// region IMPLEMENT Transfer ///////////////////////////////////////////////////////////////////////////////////////////
-
-type Transfer struct {
-	objectstorage.StorableObjectFlags
-
-	inputs     *inputs.Inputs
-	outputs    *outputs.Outputs
-	signatures *signatures.Signatures
-
-	id      *id.Id
-	idMutex sync.RWMutex
-
-	essenceBytes      []byte
-	essenceBytesMutex sync.RWMutex
-
-	signatureBytes      []byte
-	signatureBytesMutex sync.RWMutex
-
-	bytes      []byte
-	bytesMutex sync.RWMutex
-}
-
-func New(inputs *inputs.Inputs, outputs *outputs.Outputs) *Transfer {
-	return &Transfer{
-		inputs:     inputs,
-		outputs:    outputs,
-		signatures: signatures.New(),
-	}
-}
-
-func FromBytes(bytes []byte, optionalTargetObject ...*Transfer) (result *Transfer, err error, consumedBytes int) {
-	// determine the target object that will hold the unmarshaled information
-	switch len(optionalTargetObject) {
-	case 0:
-		result = &Transfer{}
-	case 1:
-		result = optionalTargetObject[0]
-	default:
-		panic("too many arguments in call to OutputFromBytes")
-	}
-
-	// initialize helper
-	marshalUtil := marshalutil.New(bytes)
-
-	// unmarshal inputs
-	parsedInputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return inputs.FromBytes(data) })
-	if err != nil {
-		return
-	}
-	result.inputs = parsedInputs.(*inputs.Inputs)
-
-	// unmarshal outputs
-	parsedOutputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return outputs.FromBytes(data) })
-	if err != nil {
-		return
-	}
-	result.outputs = parsedOutputs.(*outputs.Outputs)
-
-	// store essence bytes
-	essenceBytesCount := marshalUtil.ReadOffset()
-	result.essenceBytes = make([]byte, essenceBytesCount)
-	copy(result.essenceBytes, bytes[:essenceBytesCount])
-
-	// unmarshal outputs
-	parsedSignatures, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return signatures.FromBytes(data) })
-	if err != nil {
-		return
-	}
-	result.signatures = parsedSignatures.(*signatures.Signatures)
-
-	// store signature bytes
-	signatureBytesCount := marshalUtil.ReadOffset() - essenceBytesCount
-	result.signatureBytes = make([]byte, signatureBytesCount)
-	copy(result.signatureBytes, bytes[essenceBytesCount:essenceBytesCount+signatureBytesCount])
-
-	// return the number of bytes we processed
-	consumedBytes = essenceBytesCount + signatureBytesCount
-
-	// store bytes, so we don't have to marshal manually
-	result.bytes = bytes[:consumedBytes]
-
-	return
-}
-
-func FromStorage(key []byte) *Transfer {
-	transferId := id.New(key)
-
-	return &Transfer{
-		id: &transferId,
-	}
-}
-
-func (transfer *Transfer) GetId() id.Id {
-	// acquire lock for reading id
-	transfer.idMutex.RLock()
-
-	// return if id has been calculated already
-	if transfer.id != nil {
-		defer transfer.idMutex.RUnlock()
-
-		return *transfer.id
-	}
-
-	// switch to write lock
-	transfer.idMutex.RUnlock()
-	transfer.idMutex.Lock()
-	defer transfer.idMutex.Unlock()
-
-	// return if id has been calculated in the mean time
-	if transfer.id != nil {
-		return *transfer.id
-	}
-
-	// otherwise calculate the id
-	idBytes := blake2b.Sum256(transfer.Bytes())
-	transferId := id.New(idBytes[:])
-
-	// cache result for later calls
-	transfer.id = &transferId
-
-	return transferId
-}
-
-func (transfer *Transfer) SignaturesValid() bool {
-	signaturesValid := true
-	transfer.inputs.ForEachAddress(func(address address.Address) bool {
-		if signature, exists := transfer.signatures.Get(address); !exists || !signature.IsValid(transfer.EssenceBytes()) {
-			signaturesValid = false
-
-			return false
-		}
-
-		return true
-	})
-
-	return signaturesValid
-}
-
-func (transfer *Transfer) EssenceBytes() []byte {
-	// acquire read lock on essenceBytes
-	transfer.essenceBytesMutex.RLock()
-
-	// return essenceBytes if the object has been marshaled already
-	if transfer.essenceBytes != nil {
-		defer transfer.essenceBytesMutex.RUnlock()
-
-		return transfer.essenceBytes
-	}
-
-	// switch to write lock
-	transfer.essenceBytesMutex.RUnlock()
-	transfer.essenceBytesMutex.Lock()
-	defer transfer.essenceBytesMutex.Unlock()
-
-	// return essenceBytes if the object has been marshaled in the mean time
-	if essenceBytes := transfer.essenceBytes; essenceBytes != nil {
-		return essenceBytes
-	}
-
-	// create marshal helper
-	marshalUtil := marshalutil.New()
-
-	// marshal inputs
-	marshalUtil.WriteBytes(transfer.inputs.Bytes())
-
-	// marshal outputs
-	marshalUtil.WriteBytes(transfer.outputs.Bytes())
-
-	// store marshaled result
-	transfer.essenceBytes = marshalUtil.Bytes()
-
-	return transfer.essenceBytes
-}
-
-func (transfer *Transfer) SignatureBytes() []byte {
-	transfer.signatureBytesMutex.RLock()
-	if transfer.signatureBytes != nil {
-		defer transfer.signatureBytesMutex.RUnlock()
-
-		return transfer.signatureBytes
-	}
-
-	transfer.signatureBytesMutex.RUnlock()
-	transfer.signatureBytesMutex.Lock()
-	defer transfer.signatureBytesMutex.Unlock()
-
-	if transfer.signatureBytes != nil {
-		return transfer.signatureBytes
-	}
-
-	// generate signatures
-	transfer.signatureBytes = transfer.signatures.Bytes()
-
-	return transfer.signatureBytes
-}
-
-func (transfer *Transfer) Bytes() []byte {
-	// acquire read lock on bytes
-	transfer.bytesMutex.RLock()
-
-	// return bytes if the object has been marshaled already
-	if transfer.bytes != nil {
-		defer transfer.bytesMutex.RUnlock()
-
-		return transfer.bytes
-	}
-
-	// switch to write lock
-	transfer.bytesMutex.RUnlock()
-	transfer.bytesMutex.Lock()
-	defer transfer.bytesMutex.Unlock()
-
-	// return bytes if the object has been marshaled in the mean time
-	if bytes := transfer.bytes; bytes != nil {
-		return bytes
-	}
-
-	// create marshal helper
-	marshalUtil := marshalutil.New()
-
-	// marshal essence bytes
-	marshalUtil.WriteBytes(transfer.EssenceBytes())
-
-	// marshal signature bytes
-	marshalUtil.WriteBytes(transfer.SignatureBytes())
-
-	// store marshaled result
-	transfer.bytes = marshalUtil.Bytes()
-
-	return transfer.bytes
-}
-
-func (transfer *Transfer) Sign(signature signatures.SignatureScheme) *Transfer {
-	transfer.signatures.Add(signature.Address(), signature.Sign(transfer.EssenceBytes()))
-
-	return transfer
-}
-
-func (transfer *Transfer) String() string {
-	id := transfer.GetId()
-
-	return stringify.Struct("Transfer"+fmt.Sprintf("(%p)", transfer),
-		stringify.StructField("id", base58.Encode(id[:])),
-		stringify.StructField("inputs", transfer.inputs),
-		stringify.StructField("outputs", transfer.outputs),
-	)
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region IMPLEMENT StorableObject interface ///////////////////////////////////////////////////////////////////////////
-
-// define contract (ensure that the struct fulfills the given interface)
-var _ objectstorage.StorableObject = &Transfer{}
-
-func (transfer *Transfer) GetStorageKey() []byte {
-	id := transfer.GetId()
-
-	return id[:]
-}
-
-func (transfer *Transfer) Update(other objectstorage.StorableObject) {
-	panic("update forbidden")
-}
-
-// MarshalBinary returns a bytes representation of the transfer by implementing the encoding.BinaryMarshaler interface.
-func (transfer *Transfer) MarshalBinary() ([]byte, error) {
-	return transfer.Bytes(), nil
-}
-
-func (transfer *Transfer) UnmarshalBinary(bytes []byte) (err error) {
-	_, err, _ = FromBytes(bytes, transfer)
-
-	return
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/valuetransfer/tangle/attachment.go b/packages/binary/valuetransfer/tangle/attachment.go
new file mode 100644
index 0000000000000000000000000000000000000000..033e3f2b2303f266e7dc43c0f7b1320442da12af
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/attachment.go
@@ -0,0 +1,23 @@
+package tangle
+
+import (
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
+)
+
+// Attachment stores the information which transaction was attached by which transaction. We need this to perform
+// reverse lookups for which Payloads contain which Transactions.
+type Attachment struct {
+	transactionId transaction.Id
+	payloadId     payload.Id
+}
+
+// TransactionId returns the transaction id of this Attachment.
+func (attachment *Attachment) TransactionId() transaction.Id {
+	return attachment.transactionId
+}
+
+// PayloadId returns the payload id of this Attachment.
+func (attachment *Attachment) PayloadId() payload.Id {
+	return attachment.payloadId
+}
diff --git a/packages/binary/valuetransfer/tangle/events.go b/packages/binary/valuetransfer/tangle/events.go
index 653ae8281c6189ab82f2346a05f1bfc6d2b124fb..15e31ddd8348f6af855e3d711fd1a924e46c2797 100644
--- a/packages/binary/valuetransfer/tangle/events.go
+++ b/packages/binary/valuetransfer/tangle/events.go
@@ -4,8 +4,7 @@ import (
 	"github.com/iotaledger/hive.go/events"
 
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadmetadata"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 )
 
 type Events struct {
@@ -16,6 +15,7 @@ type Events struct {
 	PayloadMissing         *events.Event
 	PayloadUnsolidifiable  *events.Event
 	TransactionRemoved     *events.Event
+	OutputMissing          *events.Event
 }
 
 func newEvents() *Events {
@@ -26,16 +26,21 @@ func newEvents() *Events {
 		PayloadMissing:         events.NewEvent(payloadIdEvent),
 		PayloadUnsolidifiable:  events.NewEvent(payloadIdEvent),
 		TransactionRemoved:     events.NewEvent(payloadIdEvent),
+		OutputMissing:          events.NewEvent(outputIdEvent),
 	}
 }
 
 func payloadIdEvent(handler interface{}, params ...interface{}) {
-	handler.(func(payloadid.Id))(params[0].(payloadid.Id))
+	handler.(func(payload.Id))(params[0].(payload.Id))
 }
 
 func cachedPayloadEvent(handler interface{}, params ...interface{}) {
-	handler.(func(*payload.CachedObject, *payloadmetadata.CachedObject))(
-		params[0].(*payload.CachedObject).Retain(),
-		params[1].(*payloadmetadata.CachedObject).Retain(),
+	handler.(func(*payload.CachedPayload, *CachedPayloadMetadata))(
+		params[0].(*payload.CachedPayload).Retain(),
+		params[1].(*CachedPayloadMetadata).Retain(),
 	)
 }
+
+func outputIdEvent(handler interface{}, params ...interface{}) {
+	handler.(func(transaction.OutputId))(params[0].(transaction.OutputId))
+}
diff --git a/packages/binary/valuetransfer/tangle/missingoutput.go b/packages/binary/valuetransfer/tangle/missingoutput.go
new file mode 100644
index 0000000000000000000000000000000000000000..039bc60cc2685e94a5185e18c60e01020d168dec
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/missingoutput.go
@@ -0,0 +1,109 @@
+package tangle
+
+import (
+	"time"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
+)
+
+// MissingPayload represents an Output that was referenced by a Transaction, but that is missing in our object storage.
+type MissingOutput struct {
+	objectstorage.StorableObjectFlags
+
+	outputId     transaction.OutputId
+	missingSince time.Time
+}
+
+// NewMissingOutput creates a new MissingOutput object, that .
+func NewMissingOutput(outputId transaction.OutputId) *MissingOutput {
+	return &MissingOutput{
+		outputId:     outputId,
+		missingSince: time.Now(),
+	}
+}
+
+// MissingOutput unmarshals a MissingOutput from a sequence of bytes - it either creates a new object or fills the
+// optionally provided one with the parsed information.
+func MissingOutputFromBytes(bytes []byte, optionalTargetObject ...*MissingOutput) (result *MissingOutput, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &MissingOutput{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to MissingOutputFromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.outputId, err = transaction.ParseOutputId(marshalUtil); err != nil {
+		return
+	}
+	if result.missingSince, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// MissingOutputFromStorage gets called when we restore a MissingOutput from the storage. The content will be
+// unmarshaled by an external caller using the binary.MarshalBinary interface.
+func MissingOutputFromStorage(keyBytes []byte) objectstorage.StorableObject {
+	outputId, err, _ := transaction.OutputIdFromBytes(keyBytes)
+	if err != nil {
+		panic(err)
+	}
+
+	return &MissingOutput{
+		outputId: outputId,
+	}
+}
+
+// Id returns the id of the Output that is missing.
+func (missingOutput *MissingOutput) Id() transaction.OutputId {
+	return missingOutput.outputId
+}
+
+// MissingSince returns the Time since the transaction was first reported as being missing.
+func (missingOutput *MissingOutput) MissingSince() time.Time {
+	return missingOutput.missingSince
+}
+
+// Bytes marshals the MissingOutput into a sequence of bytes.
+func (missingOutput *MissingOutput) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	marshalUtil.WriteBytes(missingOutput.outputId.Bytes())
+	marshalUtil.WriteTime(missingOutput.missingSince)
+
+	return marshalUtil.Bytes()
+}
+
+// GetStorageKey returns the key that is used to store the object in the object storage.
+func (missingOutput *MissingOutput) GetStorageKey() []byte {
+	return missingOutput.outputId.Bytes()
+}
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+func (missingOutput *MissingOutput) Update(other objectstorage.StorableObject) {
+	panic("implement me")
+}
+
+// MarshalBinary returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler
+// interface.
+func (missingOutput *MissingOutput) MarshalBinary() (data []byte, err error) {
+	return missingOutput.Bytes(), nil
+}
+
+// UnmarshalBinary restores the values of a MissingOutput from a sequence of bytes using the  encoding.BinaryUnmarshaler
+// interface.
+func (missingOutput *MissingOutput) UnmarshalBinary(data []byte) (err error) {
+	_, err, _ = MissingOutputFromBytes(data, missingOutput)
+
+	return
+}
diff --git a/packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go b/packages/binary/valuetransfer/tangle/missingpayload.go
similarity index 72%
rename from packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go
rename to packages/binary/valuetransfer/tangle/missingpayload.go
index 38afa8099f50a9c59aa62e56bed61e714911449d..c4eb653276ad39e4697adfca578e3d73a6d97496 100644
--- a/packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go
+++ b/packages/binary/valuetransfer/tangle/missingpayload.go
@@ -1,4 +1,4 @@
-package missingpayload
+package tangle
 
 import (
 	"time"
@@ -6,7 +6,7 @@ import (
 	"github.com/iotaledger/hive.go/objectstorage"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
 )
 
 // MissingPayload represents a payload that was referenced through branch or trunk but that is missing in our object
@@ -14,21 +14,21 @@ import (
 type MissingPayload struct {
 	objectstorage.StorableObjectFlags
 
-	payloadId    payloadid.Id
+	payloadId    payload.Id
 	missingSince time.Time
 }
 
-// New creates an entry for a missing value transfer payload.
-func New(payloadId payloadid.Id) *MissingPayload {
+// NewMissingPayload creates an entry for a missing value transfer payload.
+func NewMissingPayload(payloadId payload.Id) *MissingPayload {
 	return &MissingPayload{
 		payloadId:    payloadId,
 		missingSince: time.Now(),
 	}
 }
 
-// FromBytes unmarshals an entry for a missing value transfer payload from a sequence of bytes.
+// MissingPayloadFromBytes unmarshals an entry for a missing value transfer payload from a sequence of bytes.
 // It either creates a new entry or fills the optionally provided one with the parsed information.
-func FromBytes(bytes []byte, optionalTargetObject ...*MissingPayload) (result *MissingPayload, err error, consumedBytes int) {
+func MissingPayloadFromBytes(bytes []byte, optionalTargetObject ...*MissingPayload) (result *MissingPayload, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -36,12 +36,12 @@ func FromBytes(bytes []byte, optionalTargetObject ...*MissingPayload) (result *M
 	case 1:
 		result = optionalTargetObject[0]
 	default:
-		panic("too many arguments in call to FromBytes")
+		panic("too many arguments in call to MissingPayloadFromBytes")
 	}
 
 	// parse the bytes
 	marshalUtil := marshalutil.New(bytes)
-	if result.payloadId, err = payloadid.Parse(marshalUtil); err != nil {
+	if result.payloadId, err = payload.ParseId(marshalUtil); err != nil {
 		return
 	}
 	if result.missingSince, err = marshalUtil.ReadTime(); err != nil {
@@ -52,14 +52,14 @@ func FromBytes(bytes []byte, optionalTargetObject ...*MissingPayload) (result *M
 	return
 }
 
-// FromStorage gets called when we restore an entry for a missing value transfer payload from the storage. The bytes and
+// MissingPayloadFromStorage gets called when we restore an entry for a missing value transfer payload from the storage. The bytes and
 // the content will be unmarshaled by an external caller using the binary.MarshalBinary interface.
-func FromStorage([]byte) objectstorage.StorableObject {
+func MissingPayloadFromStorage([]byte) objectstorage.StorableObject {
 	return &MissingPayload{}
 }
 
 // GetId returns the payload id, that is missing.
-func (missingPayload *MissingPayload) GetId() payloadid.Id {
+func (missingPayload *MissingPayload) GetId() payload.Id {
 	return missingPayload.payloadId
 }
 
@@ -97,7 +97,7 @@ func (missingPayload *MissingPayload) MarshalBinary() (data []byte, err error) {
 
 // UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface.
 func (missingPayload *MissingPayload) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = FromBytes(data, missingPayload)
+	_, err, _ = MissingPayloadFromBytes(data, missingPayload)
 
 	return
 }
diff --git a/packages/binary/valuetransfer/tangle/payloadapprover.go b/packages/binary/valuetransfer/tangle/payloadapprover.go
new file mode 100644
index 0000000000000000000000000000000000000000..e86379258ed67159df15b2e40d10313d5b2ec9a4
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadapprover.go
@@ -0,0 +1,129 @@
+package tangle
+
+import (
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
+)
+
+// PayloadApprover is a database entity, that allows us to keep track of the "tangle structure" by encoding which
+// payload approves which other payload. It allows us to traverse the tangle in the opposite direction of the referenced
+// trunk and branch payloads.
+type PayloadApprover struct {
+	objectstorage.StorableObjectFlags
+
+	storageKey          []byte
+	referencedPayloadId payload.Id
+	approvingPayloadId  payload.Id
+}
+
+// NewPayloadApprover creates an approver object that encodes a single relation between an approved and an approving payload.
+func NewPayloadApprover(referencedPayload payload.Id, approvingPayload payload.Id) *PayloadApprover {
+	marshalUtil := marshalutil.New(payload.IdLength + payload.IdLength)
+	marshalUtil.WriteBytes(referencedPayload.Bytes())
+	marshalUtil.WriteBytes(approvingPayload.Bytes())
+
+	return &PayloadApprover{
+		referencedPayloadId: referencedPayload,
+		approvingPayloadId:  approvingPayload,
+		storageKey:          marshalUtil.Bytes(),
+	}
+}
+
+// PayloadApproverFromStorage get's called when we restore transaction metadata from the storage.
+// In contrast to other database models, it unmarshals the information from the key and does not use the UnmarshalBinary
+// method.
+func PayloadApproverFromStorage(idBytes []byte) objectstorage.StorableObject {
+	marshalUtil := marshalutil.New(idBytes)
+
+	referencedPayloadId, err := payload.ParseId(marshalUtil)
+	if err != nil {
+		panic(err)
+	}
+	approvingPayloadId, err := payload.ParseId(marshalUtil)
+	if err != nil {
+		panic(err)
+	}
+
+	result := &PayloadApprover{
+		referencedPayloadId: referencedPayloadId,
+		approvingPayloadId:  approvingPayloadId,
+		storageKey:          marshalUtil.Bytes(true),
+	}
+
+	return result
+}
+
+// GetApprovingPayloadId returns the id of the approving payload.
+func (payloadApprover *PayloadApprover) GetApprovingPayloadId() payload.Id {
+	return payloadApprover.approvingPayloadId
+}
+
+// GetStorageKey returns the key that is used to store the object in the database.
+// It is required to match StorableObject interface.
+func (payloadApprover *PayloadApprover) GetStorageKey() []byte {
+	return payloadApprover.storageKey
+}
+
+// MarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
+// since all of the information about an approver are stored in the "key".
+func (payloadApprover *PayloadApprover) MarshalBinary() (data []byte, err error) {
+	return
+}
+
+// UnmarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
+// since all of the information about an approver are stored in the "key".
+func (payloadApprover *PayloadApprover) UnmarshalBinary(data []byte) error {
+	return nil
+}
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+// It is required to match StorableObject interface.
+func (payloadApprover *PayloadApprover) Update(other objectstorage.StorableObject) {
+	panic("implement me")
+}
+
+// CachedPayloadApprover is a wrapper for the object storage, that takes care of type casting the managed objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of CachedApprovers, without having to manually type cast over and over again.
+type CachedPayloadApprover struct {
+	objectstorage.CachedObject
+}
+
+// Retain wraps the underlying method to return a new "wrapped object".
+func (cachedPayloadApprover *CachedPayloadApprover) Retain() *CachedPayloadApprover {
+	return &CachedPayloadApprover{cachedPayloadApprover.CachedObject.Retain()}
+}
+
+// Consume wraps the underlying method to return the correctly typed objects in the callback.
+func (cachedPayloadApprover *CachedPayloadApprover) Consume(consumer func(payload *PayloadApprover)) bool {
+	return cachedPayloadApprover.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*PayloadApprover))
+	})
+}
+
+// Unwrap provides a way to "Get" a type casted version of the underlying object.
+func (cachedPayloadApprover *CachedPayloadApprover) Unwrap() *PayloadApprover {
+	if untypedTransaction := cachedPayloadApprover.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*PayloadApprover); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
+
+type CachedApprovers []*CachedPayloadApprover
+
+func (cachedApprovers CachedApprovers) Consume(consumer func(approver *PayloadApprover)) (consumed bool) {
+	for _, cachedApprover := range cachedApprovers {
+		consumed = cachedApprover.Consume(consumer) || consumed
+	}
+
+	return
+}
diff --git a/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go b/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go
deleted file mode 100644
index b90c704272f82869a9b3069f59d1409d92650b6f..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package payloadapprover
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-// CachedObject is a wrapper for the object storage, that takes care of type casting the managed objects.
-// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
-// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
-// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
-// specialized types of CachedObjects, without having to manually type cast over and over again.
-type CachedObject struct {
-	objectstorage.CachedObject
-}
-
-// Retain wraps the underlying method to return a new "wrapped object".
-func (cachedApprover *CachedObject) Retain() *CachedObject {
-	return &CachedObject{cachedApprover.CachedObject.Retain()}
-}
-
-// Consume wraps the underlying method to return the correctly typed objects in the callback.
-func (cachedApprover *CachedObject) Consume(consumer func(payload *PayloadApprover)) bool {
-	return cachedApprover.CachedObject.Consume(func(object objectstorage.StorableObject) {
-		consumer(object.(*PayloadApprover))
-	})
-}
-
-// Unwrap provides a way to "Get" a type casted version of the underlying object.
-func (cachedApprover *CachedObject) Unwrap() *PayloadApprover {
-	if untypedTransaction := cachedApprover.Get(); untypedTransaction == nil {
-		return nil
-	} else {
-		if typeCastedTransaction := untypedTransaction.(*PayloadApprover); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
-			return nil
-		} else {
-			return typeCastedTransaction
-		}
-	}
-}
-
-type CachedObjects []*CachedObject
-
-func (cachedApprovers CachedObjects) Consume(consumer func(approver *PayloadApprover)) (consumed bool) {
-	for _, cachedApprover := range cachedApprovers {
-		consumed = cachedApprover.Consume(consumer) || consumed
-	}
-
-	return
-}
diff --git a/packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go b/packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go
deleted file mode 100644
index 7c92ae68b2b5bd01615f73532b5edb579aa5219b..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package payloadapprover
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-)
-
-// PayloadApprover is a database entity, that allows us to keep track of the "tangle structure" by encoding which
-// payload approves which other payload. It allows us to traverse the tangle in the opposite direction of the referenced
-// trunk and branch payloads.
-type PayloadApprover struct {
-	objectstorage.StorableObjectFlags
-
-	storageKey          []byte
-	referencedPayloadId payloadid.Id
-	approvingPayloadId  payloadid.Id
-}
-
-// New creates an approver object that encodes a single relation between an approved and an approving payload.
-func New(referencedPayload payloadid.Id, approvingPayload payloadid.Id) *PayloadApprover {
-	marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length)
-	marshalUtil.WriteBytes(referencedPayload.Bytes())
-	marshalUtil.WriteBytes(approvingPayload.Bytes())
-
-	return &PayloadApprover{
-		referencedPayloadId: referencedPayload,
-		approvingPayloadId:  approvingPayload,
-		storageKey:          marshalUtil.Bytes(),
-	}
-}
-
-// FromStorage get's called when we restore transaction metadata from the storage.
-// In contrast to other database models, it unmarshals the information from the key and does not use the UnmarshalBinary
-// method.
-func FromStorage(idBytes []byte) objectstorage.StorableObject {
-	marshalUtil := marshalutil.New(idBytes)
-
-	referencedPayloadId, err := payloadid.Parse(marshalUtil)
-	if err != nil {
-		panic(err)
-	}
-	approvingPayloadId, err := payloadid.Parse(marshalUtil)
-	if err != nil {
-		panic(err)
-	}
-
-	result := &PayloadApprover{
-		referencedPayloadId: referencedPayloadId,
-		approvingPayloadId:  approvingPayloadId,
-		storageKey:          marshalUtil.Bytes(true),
-	}
-
-	return result
-}
-
-// GetApprovingPayloadId returns the id of the approving payload.
-func (approver *PayloadApprover) GetApprovingPayloadId() payloadid.Id {
-	return approver.approvingPayloadId
-}
-
-// GetStorageKey returns the key that is used to store the object in the database.
-// It is required to match StorableObject interface.
-func (approver *PayloadApprover) GetStorageKey() []byte {
-	return approver.storageKey
-}
-
-// MarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
-// since all of the information about an approver are stored in the "key".
-func (approver *PayloadApprover) MarshalBinary() (data []byte, err error) {
-	return
-}
-
-// UnmarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
-// since all of the information about an approver are stored in the "key".
-func (approver *PayloadApprover) UnmarshalBinary(data []byte) error {
-	return nil
-}
-
-// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
-// It is required to match StorableObject interface.
-func (approver *PayloadApprover) Update(other objectstorage.StorableObject) {
-	panic("implement me")
-}
diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go b/packages/binary/valuetransfer/tangle/payloadmetadata.go
similarity index 59%
rename from packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go
rename to packages/binary/valuetransfer/tangle/payloadmetadata.go
index b6eb53fbf71dc683f8c3b2479626b97c371e9b56..d693c86ef09f99f746aff937840be6c690709219 100644
--- a/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go
+++ b/packages/binary/valuetransfer/tangle/payloadmetadata.go
@@ -1,4 +1,4 @@
-package payloadmetadata
+package tangle
 
 import (
 	"sync"
@@ -8,7 +8,7 @@ import (
 	"github.com/iotaledger/hive.go/stringify"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
 )
 
 // PayloadMetadata is a container for the metadata of a value transfer payload.
@@ -16,7 +16,7 @@ import (
 type PayloadMetadata struct {
 	objectstorage.StorableObjectFlags
 
-	payloadId          payloadid.Id
+	payloadId          payload.Id
 	solid              bool
 	solidificationTime time.Time
 
@@ -24,16 +24,16 @@ type PayloadMetadata struct {
 	solidificationTimeMutex sync.RWMutex
 }
 
-// New creates an empty container for the metadata of a value transfer payload.
-func New(payloadId payloadid.Id) *PayloadMetadata {
+// NewPayloadMetadata creates an empty container for the metadata of a value transfer payload.
+func NewPayloadMetadata(payloadId payload.Id) *PayloadMetadata {
 	return &PayloadMetadata{
 		payloadId: payloadId,
 	}
 }
 
-// FromBytes unmarshals a container with the metadata of a value transfer payload from a sequence of bytes.
+// PayloadMetadataFromBytes unmarshals a container with the metadata of a value transfer payload from a sequence of bytes.
 // It either creates a new container or fills the optionally provided container with the parsed information.
-func FromBytes(bytes []byte, optionalTargetObject ...*PayloadMetadata) (result *PayloadMetadata, err error, consumedBytes int) {
+func PayloadMetadataFromBytes(bytes []byte, optionalTargetObject ...*PayloadMetadata) (result *PayloadMetadata, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -41,12 +41,12 @@ func FromBytes(bytes []byte, optionalTargetObject ...*PayloadMetadata) (result *
 	case 1:
 		result = optionalTargetObject[0]
 	default:
-		panic("too many arguments in call to FromBytes")
+		panic("too many arguments in call to PayloadMetadataFromBytes")
 	}
 
 	// parse the bytes
 	marshalUtil := marshalutil.New(bytes)
-	if result.payloadId, err = payloadid.Parse(marshalUtil); err != nil {
+	if result.payloadId, err = payload.ParseId(marshalUtil); err != nil {
 		return
 	}
 	if result.solidificationTime, err = marshalUtil.ReadTime(); err != nil {
@@ -60,22 +60,22 @@ func FromBytes(bytes []byte, optionalTargetObject ...*PayloadMetadata) (result *
 	return
 }
 
-// FromStorage gets called when we restore transaction metadata from the storage. The bytes and the content will be
+// PayloadMetadataFromStorage gets called when we restore transaction metadata from the storage. The bytes and the content will be
 // unmarshaled by an external caller using the binary.MarshalBinary interface.
-func FromStorage(id []byte) objectstorage.StorableObject {
+func PayloadMetadataFromStorage(id []byte) objectstorage.StorableObject {
 	result := &PayloadMetadata{}
 
 	var err error
-	if result.payloadId, err = payloadid.Parse(marshalutil.New(id)); err != nil {
+	if result.payloadId, err = payload.ParseId(marshalutil.New(id)); err != nil {
 		panic(err)
 	}
 
 	return result
 }
 
-// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
-func Parse(marshalUtil *marshalutil.MarshalUtil) (*PayloadMetadata, error) {
-	if payloadMetadata, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
+// ParsePayloadMetadata is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+func ParsePayloadMetadata(marshalUtil *marshalutil.MarshalUtil) (*PayloadMetadata, error) {
+	if payloadMetadata, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return PayloadMetadataFromBytes(data) }); err != nil {
 		return nil, err
 	} else {
 		return payloadMetadata.(*PayloadMetadata), nil
@@ -83,11 +83,11 @@ func Parse(marshalUtil *marshalutil.MarshalUtil) (*PayloadMetadata, error) {
 }
 
 // GetPayloadId return the id of the payload that this metadata is associated to.
-func (payloadMetadata *PayloadMetadata) GetPayloadId() payloadid.Id {
+func (payloadMetadata *PayloadMetadata) GetPayloadId() payload.Id {
 	return payloadMetadata.payloadId
 }
 
-// IsSolid returns true of the payload has been marked as solid.
+// IsSolid returns true if the payload has been marked as solid.
 func (payloadMetadata *PayloadMetadata) IsSolid() (result bool) {
 	payloadMetadata.solidMutex.RLock()
 	result = payloadMetadata.solid
@@ -172,7 +172,41 @@ func (payloadMetadata *PayloadMetadata) MarshalBinary() ([]byte, error) {
 
 // UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface.
 func (payloadMetadata *PayloadMetadata) UnmarshalBinary(data []byte) (err error) {
-	_, err, _ = FromBytes(data, payloadMetadata)
+	_, err, _ = PayloadMetadataFromBytes(data, payloadMetadata)
 
 	return
 }
+
+// CachedPayloadMetadata is a wrapper for the object storage, that takes care of type casting the managed objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of CachedObjects, without having to manually type cast over and over again.
+type CachedPayloadMetadata struct {
+	objectstorage.CachedObject
+}
+
+// Retain wraps the underlying method to return a new "wrapped object".
+func (cachedPayloadMetadata *CachedPayloadMetadata) Retain() *CachedPayloadMetadata {
+	return &CachedPayloadMetadata{cachedPayloadMetadata.CachedObject.Retain()}
+}
+
+// Consume wraps the underlying method to return the correctly typed objects in the callback.
+func (cachedPayloadMetadata *CachedPayloadMetadata) Consume(consumer func(payload *PayloadMetadata)) bool {
+	return cachedPayloadMetadata.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*PayloadMetadata))
+	})
+}
+
+// Unwrap provides a way to "Get" a type casted version of the underlying object.
+func (cachedPayloadMetadata *CachedPayloadMetadata) Unwrap() *PayloadMetadata {
+	if untypedTransaction := cachedPayloadMetadata.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*PayloadMetadata); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go b/packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go
deleted file mode 100644
index bde06d7cc7f75d5508858a09bdd8c2b52d51c274..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package payloadmetadata
-
-import (
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-// CachedObject is a wrapper for the object storage, that takes care of type casting the managed objects.
-// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
-// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
-// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
-// specialized types of CachedObjects, without having to manually type cast over and over again.
-type CachedObject struct {
-	objectstorage.CachedObject
-}
-
-// Retain wraps the underlying method to return a new "wrapped object".
-func (cachedPayload *CachedObject) Retain() *CachedObject {
-	return &CachedObject{cachedPayload.CachedObject.Retain()}
-}
-
-// Consume wraps the underlying method to return the correctly typed objects in the callback.
-func (cachedPayload *CachedObject) Consume(consumer func(payload *PayloadMetadata)) bool {
-	return cachedPayload.CachedObject.Consume(func(object objectstorage.StorableObject) {
-		consumer(object.(*PayloadMetadata))
-	})
-}
-
-// Unwrap provides a way to "Get" a type casted version of the underlying object.
-func (cachedPayload *CachedObject) Unwrap() *PayloadMetadata {
-	if untypedTransaction := cachedPayload.Get(); untypedTransaction == nil {
-		return nil
-	} else {
-		if typeCastedTransaction := untypedTransaction.(*PayloadMetadata); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
-			return nil
-		} else {
-			return typeCastedTransaction
-		}
-	}
-}
diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go b/packages/binary/valuetransfer/tangle/payloadmetadata_test.go
similarity index 80%
rename from packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go
rename to packages/binary/valuetransfer/tangle/payloadmetadata_test.go
index fd85c6f5307055506ea8dc258d4bc42e8c7552bb..cd50258b525079014277f18d758be15154708454 100644
--- a/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go
+++ b/packages/binary/valuetransfer/tangle/payloadmetadata_test.go
@@ -1,4 +1,4 @@
-package payloadmetadata
+package tangle
 
 import (
 	"testing"
@@ -6,13 +6,13 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
 )
 
 func TestMarshalUnmarshal(t *testing.T) {
-	originalMetadata := New(id.Genesis)
+	originalMetadata := NewPayloadMetadata(payload.GenesisId)
 
-	clonedMetadata, err, _ := FromBytes(originalMetadata.Bytes())
+	clonedMetadata, err, _ := PayloadMetadataFromBytes(originalMetadata.Bytes())
 	if err != nil {
 		panic(err)
 	}
@@ -23,7 +23,7 @@ func TestMarshalUnmarshal(t *testing.T) {
 
 	originalMetadata.SetSolid(true)
 
-	clonedMetadata, err, _ = FromBytes(originalMetadata.Bytes())
+	clonedMetadata, err, _ = PayloadMetadataFromBytes(originalMetadata.Bytes())
 	if err != nil {
 		panic(err)
 	}
@@ -34,7 +34,7 @@ func TestMarshalUnmarshal(t *testing.T) {
 }
 
 func TestPayloadMetadata_SetSolid(t *testing.T) {
-	originalMetadata := New(id.Genesis)
+	originalMetadata := NewPayloadMetadata(payload.GenesisId)
 
 	assert.Equal(t, false, originalMetadata.IsSolid())
 	assert.Equal(t, time.Time{}, originalMetadata.GetSoldificationTime())
diff --git a/packages/binary/valuetransfer/tangle/tangle.go b/packages/binary/valuetransfer/tangle/tangle.go
index 39c97d7197cca9e091e3b386f319b965fb7ce280..5c61b2b713d39b43e8e6d1b9de471e3bcd7de928 100644
--- a/packages/binary/valuetransfer/tangle/tangle.go
+++ b/packages/binary/valuetransfer/tangle/tangle.go
@@ -2,6 +2,7 @@ package tangle
 
 import (
 	"container/list"
+	"fmt"
 	"time"
 
 	"github.com/dgraph-io/badger/v2"
@@ -9,11 +10,8 @@ import (
 	"github.com/iotaledger/hive.go/objectstorage"
 
 	"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
-	valuepayload "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/missingpayload"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadapprover"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadmetadata"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 )
 
 // Tangle represents the value tangle that consists out of value payloads.
@@ -26,6 +24,9 @@ type Tangle struct {
 	approverStorage        *objectstorage.ObjectStorage
 	missingPayloadStorage  *objectstorage.ObjectStorage
 
+	transactionOutputMetadataStorage *objectstorage.ObjectStorage
+	missingOutputStorage             *objectstorage.ObjectStorage
+
 	Events Events
 
 	storePayloadWorkerPool async.WorkerPool
@@ -37,10 +38,13 @@ func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) {
 	result = &Tangle{
 		storageId: storageId,
 
-		payloadStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayload...), valuepayload.FromStorage, objectstorage.CacheTime(time.Second)),
-		payloadMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayloadMetadata...), payloadmetadata.FromStorage, objectstorage.CacheTime(time.Second)),
-		approverStorage:        objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferApprover...), payloadapprover.FromStorage, objectstorage.CacheTime(time.Second), objectstorage.PartitionKey(payloadid.Length, payloadid.Length), objectstorage.KeysOnly(true)),
-		missingPayloadStorage:  objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), missingpayload.FromStorage, objectstorage.CacheTime(time.Second)),
+		payloadStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayload...), payload.FromStorage, objectstorage.CacheTime(time.Second)),
+		payloadMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayloadMetadata...), PayloadMetadataFromStorage, objectstorage.CacheTime(time.Second)),
+		approverStorage:        objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferApprover...), PayloadApproverFromStorage, objectstorage.CacheTime(time.Second), objectstorage.PartitionKey(payload.IdLength, payload.IdLength), objectstorage.KeysOnly(true)),
+		missingPayloadStorage:  objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingPayloadFromStorage, objectstorage.CacheTime(time.Second)),
+
+		transactionOutputMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), transaction.OutputFromStorage, objectstorage.CacheTime(time.Second)),
+		missingOutputStorage:             objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingOutputFromStorage, objectstorage.CacheTime(time.Second)),
 
 		Events: *newEvents(),
 	}
@@ -49,25 +53,25 @@ func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) {
 }
 
 // AttachPayload adds a new payload to the value tangle.
-func (tangle *Tangle) AttachPayload(payload *valuepayload.Payload) {
+func (tangle *Tangle) AttachPayload(payload *payload.Payload) {
 	tangle.storePayloadWorkerPool.Submit(func() { tangle.storePayloadWorker(payload) })
 }
 
 // GetPayload retrieves a payload from the object storage.
-func (tangle *Tangle) GetPayload(payloadId payloadid.Id) *valuepayload.CachedObject {
-	return &valuepayload.CachedObject{CachedObject: tangle.payloadStorage.Load(payloadId.Bytes())}
+func (tangle *Tangle) GetPayload(payloadId payload.Id) *payload.CachedPayload {
+	return &payload.CachedPayload{CachedObject: tangle.payloadStorage.Load(payloadId.Bytes())}
 }
 
 // GetPayloadMetadata retrieves the metadata of a value payload from the object storage.
-func (tangle *Tangle) GetPayloadMetadata(payloadId payloadid.Id) *payloadmetadata.CachedObject {
-	return &payloadmetadata.CachedObject{CachedObject: tangle.payloadMetadataStorage.Load(payloadId.Bytes())}
+func (tangle *Tangle) GetPayloadMetadata(payloadId payload.Id) *CachedPayloadMetadata {
+	return &CachedPayloadMetadata{CachedObject: tangle.payloadMetadataStorage.Load(payloadId.Bytes())}
 }
 
 // GetApprovers retrieves the approvers of a payload from the object storage.
-func (tangle *Tangle) GetApprovers(transactionId payloadid.Id) payloadapprover.CachedObjects {
-	approvers := make(payloadapprover.CachedObjects, 0)
+func (tangle *Tangle) GetApprovers(transactionId payload.Id) CachedApprovers {
+	approvers := make(CachedApprovers, 0)
 	tangle.approverStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool {
-		approvers = append(approvers, &payloadapprover.CachedObject{CachedObject: cachedObject})
+		approvers = append(approvers, &CachedPayloadApprover{CachedObject: cachedObject})
 
 		return true
 	}, transactionId[:])
@@ -106,26 +110,26 @@ func (tangle *Tangle) Prune() error {
 }
 
 // storePayloadWorker is the worker function that stores the payload and calls the corresponding storage events.
-func (tangle *Tangle) storePayloadWorker(payload *valuepayload.Payload) {
+func (tangle *Tangle) storePayloadWorker(payloadToStore *payload.Payload) {
 	// store payload
-	var cachedPayload *valuepayload.CachedObject
-	if _tmp, transactionIsNew := tangle.payloadStorage.StoreIfAbsent(payload); !transactionIsNew {
+	var cachedPayload *payload.CachedPayload
+	if _tmp, transactionIsNew := tangle.payloadStorage.StoreIfAbsent(payloadToStore); !transactionIsNew {
 		return
 	} else {
-		cachedPayload = &valuepayload.CachedObject{CachedObject: _tmp}
+		cachedPayload = &payload.CachedPayload{CachedObject: _tmp}
 	}
 
 	// store payload metadata
-	payloadId := payload.GetId()
-	cachedMetadata := &payloadmetadata.CachedObject{CachedObject: tangle.payloadMetadataStorage.Store(payloadmetadata.New(payloadId))}
+	payloadId := payloadToStore.Id()
+	cachedMetadata := &CachedPayloadMetadata{CachedObject: tangle.payloadMetadataStorage.Store(NewPayloadMetadata(payloadId))}
 
 	// store trunk approver
-	trunkId := payload.GetTrunkId()
-	tangle.approverStorage.Store(payloadapprover.New(trunkId, payloadId)).Release()
+	trunkId := payloadToStore.TrunkId()
+	tangle.approverStorage.Store(NewPayloadApprover(trunkId, payloadId)).Release()
 
 	// store branch approver
-	if branchId := payload.GetBranchId(); branchId != trunkId {
-		tangle.approverStorage.Store(payloadapprover.New(branchId, trunkId)).Release()
+	if branchId := payloadToStore.BranchId(); branchId != trunkId {
+		tangle.approverStorage.Store(NewPayloadApprover(branchId, trunkId)).Release()
 	}
 
 	// trigger events
@@ -134,21 +138,39 @@ func (tangle *Tangle) storePayloadWorker(payload *valuepayload.Payload) {
 	}
 	tangle.Events.PayloadAttached.Trigger(cachedPayload, cachedMetadata)
 
+	// retrieve or store TransactionMetadata
+	newTransaction := false
+	transactionId := cachedPayload.Unwrap().Transaction().Id()
+	cachedTransactionMetadata := &CachedTransactionMetadata{CachedObject: tangle.payloadMetadataStorage.ComputeIfAbsent(transactionId.Bytes(), func(key []byte) objectstorage.StorableObject {
+		newTransaction = true
+
+		result := NewTransactionMetadata(transactionId)
+		result.Persist()
+		result.SetModified()
+
+		return result
+	})}
+
+	// if the transaction is new, store the Consumers.
+	if newTransaction {
+		fmt.Println("git aWADD")
+	}
+
 	// check solidity
 	tangle.solidifierWorkerPool.Submit(func() {
-		tangle.solidifyTransactionWorker(cachedPayload, cachedMetadata)
+		tangle.solidifyTransactionWorker(cachedPayload, cachedMetadata, cachedTransactionMetadata)
 	})
 }
 
 // solidifyTransactionWorker is the worker function that solidifies the payloads (recursively from past to present).
-func (tangle *Tangle) solidifyTransactionWorker(cachedPayload *valuepayload.CachedObject, cachedMetadata *payloadmetadata.CachedObject) {
-	popElementsFromStack := func(stack *list.List) (*valuepayload.CachedObject, *payloadmetadata.CachedObject) {
+func (tangle *Tangle) solidifyTransactionWorker(cachedPayload *payload.CachedPayload, cachedMetadata *CachedPayloadMetadata, cachedTransactionMetadata *CachedTransactionMetadata) {
+	popElementsFromStack := func(stack *list.List) (*payload.CachedPayload, *CachedPayloadMetadata) {
 		currentSolidificationEntry := stack.Front()
 		currentCachedPayload := currentSolidificationEntry.Value.([2]interface{})[0]
 		currentCachedMetadata := currentSolidificationEntry.Value.([2]interface{})[1]
 		stack.Remove(currentSolidificationEntry)
 
-		return currentCachedPayload.(*valuepayload.CachedObject), currentCachedMetadata.(*payloadmetadata.CachedObject)
+		return currentCachedPayload.(*payload.CachedPayload), currentCachedMetadata.(*CachedPayloadMetadata)
 	}
 
 	// initialize the stack
@@ -164,33 +186,74 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedPayload *valuepayload.Cach
 		if currentPayload == nil || currentMetadata == nil {
 			currentCachedPayload.Release()
 			currentCachedMetadata.Release()
+			cachedTransactionMetadata.Release()
 
 			continue
 		}
 
 		// if current transaction is solid and was not marked as solid before: mark as solid and propagate
-		if tangle.isPayloadSolid(currentPayload, currentMetadata) && currentMetadata.SetSolid(true) {
-			tangle.Events.PayloadSolid.Trigger(currentCachedPayload, currentCachedMetadata)
+		if tangle.isPayloadSolid(currentPayload, currentMetadata) && tangle.isTransactionSolid(currentPayload.Transaction(), cachedTransactionMetadata.Unwrap()) {
+			if currentMetadata.SetSolid(true) {
+				tangle.Events.PayloadSolid.Trigger(currentCachedPayload, currentCachedMetadata)
 
-			tangle.GetApprovers(currentPayload.GetId()).Consume(func(approver *payloadapprover.PayloadApprover) {
-				approverTransactionId := approver.GetApprovingPayloadId()
+				tangle.GetApprovers(currentPayload.Id()).Consume(func(approver *PayloadApprover) {
+					approverTransactionId := approver.GetApprovingPayloadId()
 
-				solidificationStack.PushBack([2]interface{}{
-					tangle.GetPayload(approverTransactionId),
-					tangle.GetPayloadMetadata(approverTransactionId),
+					solidificationStack.PushBack([2]interface{}{
+						tangle.GetPayload(approverTransactionId),
+						tangle.GetPayloadMetadata(approverTransactionId),
+					})
 				})
-			})
+			}
 		}
 
 		// release cached results
 		currentCachedPayload.Release()
 		currentCachedMetadata.Release()
+		cachedTransactionMetadata.Release()
+	}
+}
+
+func (tangle *Tangle) isTransactionSolid(transaction *transaction.Transaction, metadata *TransactionMetadata) bool {
+	if transaction == nil || transaction.IsDeleted() {
+		return false
+	}
+
+	if metadata == nil || metadata.IsDeleted() {
+		return false
 	}
+
+	if metadata.Solid() {
+		return true
+	}
+
+	// iterate through all transfers and check if they are solid
+	return transaction.Inputs().ForEach(tangle.isOutputMarkedAsSolid)
+}
+
+func (tangle *Tangle) GetTransferOutputMetadata(transactionOutputId transaction.OutputId) *CachedTransactionOutputMetadata {
+	return &CachedTransactionOutputMetadata{CachedObject: tangle.transactionOutputMetadataStorage.Load(transactionOutputId.Bytes())}
+}
+
+func (tangle *Tangle) isOutputMarkedAsSolid(transferOutputId transaction.OutputId) (result bool) {
+	objectConsumed := tangle.GetTransferOutputMetadata(transferOutputId).Consume(func(transferOutputMetadata *TransactionOutputMetadata) {
+		result = transferOutputMetadata.Solid()
+	})
+
+	if !objectConsumed {
+		if cachedMissingOutput, missingOutputStored := tangle.missingOutputStorage.StoreIfAbsent(NewMissingOutput(transferOutputId)); missingOutputStored {
+			cachedMissingOutput.Consume(func(object objectstorage.StorableObject) {
+				tangle.Events.OutputMissing.Trigger(object.(*MissingOutput).Id())
+			})
+		}
+	}
+
+	return
 }
 
 // isPayloadSolid returns true if the given payload is solid. A payload is considered to be solid solid, if it is either
 // already marked as solid or if its referenced payloads are marked as solid.
-func (tangle *Tangle) isPayloadSolid(payload *valuepayload.Payload, metadata *payloadmetadata.PayloadMetadata) bool {
+func (tangle *Tangle) isPayloadSolid(payload *payload.Payload, metadata *PayloadMetadata) bool {
 	if payload == nil || payload.IsDeleted() {
 		return false
 	}
@@ -203,13 +266,13 @@ func (tangle *Tangle) isPayloadSolid(payload *valuepayload.Payload, metadata *pa
 		return true
 	}
 
-	return tangle.isPayloadMarkedAsSolid(payload.GetTrunkId()) && tangle.isPayloadMarkedAsSolid(payload.GetBranchId())
+	return tangle.isPayloadMarkedAsSolid(payload.TrunkId()) && tangle.isPayloadMarkedAsSolid(payload.BranchId())
 }
 
 // isPayloadMarkedAsSolid returns true if the payload was marked as solid already (by setting the corresponding flags
 // in its metadata.
-func (tangle *Tangle) isPayloadMarkedAsSolid(payloadId payloadid.Id) bool {
-	if payloadId == payloadid.Genesis {
+func (tangle *Tangle) isPayloadMarkedAsSolid(payloadId payload.Id) bool {
+	if payloadId == payload.GenesisId {
 		return true
 	}
 
@@ -218,9 +281,9 @@ func (tangle *Tangle) isPayloadMarkedAsSolid(payloadId payloadid.Id) bool {
 		transactionMetadataCached.Release()
 
 		// if transaction is missing and was not reported as missing, yet
-		if cachedMissingPayload, missingPayloadStored := tangle.missingPayloadStorage.StoreIfAbsent(missingpayload.New(payloadId)); missingPayloadStored {
+		if cachedMissingPayload, missingPayloadStored := tangle.missingPayloadStorage.StoreIfAbsent(NewMissingPayload(payloadId)); missingPayloadStored {
 			cachedMissingPayload.Consume(func(object objectstorage.StorableObject) {
-				tangle.Events.PayloadMissing.Trigger(object.(*missingpayload.MissingPayload).GetId())
+				tangle.Events.PayloadMissing.Trigger(object.(*MissingPayload).GetId())
 			})
 		}
 
diff --git a/packages/binary/valuetransfer/tangle/tangle_test.go b/packages/binary/valuetransfer/tangle/tangle_test.go
index ccbbea0ad1f4bb84cb3f36eb20a7422695595524..cd83c7796dc7d3b5797bb7bcf652cd69798a3c4b 100644
--- a/packages/binary/valuetransfer/tangle/tangle_test.go
+++ b/packages/binary/valuetransfer/tangle/tangle_test.go
@@ -11,16 +11,9 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance/color"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/inputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/outputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadmetadata"
-	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transferoutput/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
 	"github.com/iotaledger/goshimmer/packages/database"
 	"github.com/iotaledger/goshimmer/plugins/config"
 )
@@ -39,7 +32,7 @@ func TestTangle_AttachPayload(t *testing.T) {
 		return
 	}
 
-	tangle.Events.PayloadSolid.Attach(events.NewClosure(func(payload *payload.CachedObject, metadata *payloadmetadata.CachedObject) {
+	tangle.Events.PayloadSolid.Attach(events.NewClosure(func(payload *payload.CachedPayload, metadata *CachedPayloadMetadata) {
 		fmt.Println(payload.Unwrap())
 
 		payload.Release()
@@ -49,15 +42,18 @@ func TestTangle_AttachPayload(t *testing.T) {
 	addressKeyPair1 := ed25119.GenerateKeyPair()
 	addressKeyPair2 := ed25119.GenerateKeyPair()
 
-	tangle.AttachPayload(payload.New(id.Genesis, id.Genesis, transfer.New(
-		inputs.New(
-			transferoutputid.New(address.FromED25519PubKey(addressKeyPair1.PublicKey), transferid.New([]byte("transfer1"))),
-			transferoutputid.New(address.FromED25519PubKey(addressKeyPair2.PublicKey), transferid.New([]byte("transfer2"))),
+	transferId1, _ := transaction.IdFromBase58("8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh")
+	transferId2, _ := transaction.IdFromBase58("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM")
+
+	tangle.AttachPayload(payload.New(payload.GenesisId, payload.GenesisId, transaction.New(
+		transaction.NewInputs(
+			transaction.NewOutputId(address.FromED25519PubKey(addressKeyPair1.PublicKey), transferId1),
+			transaction.NewOutputId(address.FromED25519PubKey(addressKeyPair2.PublicKey), transferId2),
 		),
 
-		outputs.New(map[address.Address][]*coloredbalance.ColoredBalance{
+		transaction.NewOutputs(map[address.Address][]*balance.Balance{
 			address.Random(): {
-				coloredbalance.New(color.IOTA, 1337),
+				balance.New(balance.COLOR_IOTA, 1337),
 			},
 		}),
 	)))
diff --git a/packages/binary/valuetransfer/tangle/transactionmetadata.go b/packages/binary/valuetransfer/tangle/transactionmetadata.go
new file mode 100644
index 0000000000000000000000000000000000000000..898c57a7002705de783894a1cd0a2b915d2c4ef8
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/transactionmetadata.go
@@ -0,0 +1,213 @@
+package tangle
+
+import (
+	"sync"
+	"time"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
+)
+
+// TransactionMetadata contains the information of a Transaction, that are based on our local perception of things (i.e. if it is
+// solid, or when we it became solid).
+type TransactionMetadata struct {
+	objectstorage.StorableObjectFlags
+
+	id                 transaction.Id
+	solid              bool
+	solidificationTime time.Time
+
+	solidMutex              sync.RWMutex
+	solidificationTimeMutex sync.RWMutex
+}
+
+// NewTransactionMetadata is the constructor for the TransactionMetadata type.
+func NewTransactionMetadata(id transaction.Id) *TransactionMetadata {
+	return &TransactionMetadata{
+		id: id,
+	}
+}
+
+// TransactionMetadataFromBytes unmarshals a TransactionMetadata object from a sequence of bytes.
+// It either creates a new object or fills the optionally provided object with the parsed information.
+func TransactionMetadataFromBytes(bytes []byte, optionalTargetObject ...*TransactionMetadata) (result *TransactionMetadata, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &TransactionMetadata{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to TransactionMetadataFromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.id, err = transaction.ParseId(marshalUtil); err != nil {
+		return
+	}
+	if result.solidificationTime, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	if result.solid, err = marshalUtil.ReadBool(); err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// TransactionMetadataFromStorage is the factory method for TransactionMetadata objects stored in the objectstorage. The bytes and the content
+// will be filled by the objectstorage, by subsequently calling MarshalBinary.
+func TransactionMetadataFromStorage(storageKey []byte) objectstorage.StorableObject {
+	result := &TransactionMetadata{}
+
+	var err error
+	if result.id, err = transaction.ParseId(marshalutil.New(storageKey)); err != nil {
+		panic(err)
+	}
+
+	return result
+}
+
+// Parse is a wrapper for simplified unmarshaling of TransactionMetadata objects from a byte stream using the marshalUtil package.
+func ParseTransactionMetadata(marshalUtil *marshalutil.MarshalUtil) (*TransactionMetadata, error) {
+	if metadata, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return TransactionMetadataFromBytes(data) }); err != nil {
+		return nil, err
+	} else {
+		return metadata.(*TransactionMetadata), nil
+	}
+}
+
+// Id return the id of the Transaction that this TransactionMetadata is associated to.
+func (transactionMetadata *TransactionMetadata) Id() transaction.Id {
+	return transactionMetadata.id
+}
+
+// Solid returns true if the Transaction has been marked as solid.
+func (transactionMetadata *TransactionMetadata) Solid() (result bool) {
+	transactionMetadata.solidMutex.RLock()
+	result = transactionMetadata.solid
+	transactionMetadata.solidMutex.RUnlock()
+
+	return
+}
+
+// SetSolid marks a Transaction as either solid or not solid.
+// It returns true if the solid flag was changes and automatically updates the solidificationTime as well.
+func (transactionMetadata *TransactionMetadata) SetSolid(solid bool) (modified bool) {
+	transactionMetadata.solidMutex.RLock()
+	if transactionMetadata.solid != solid {
+		transactionMetadata.solidMutex.RUnlock()
+
+		transactionMetadata.solidMutex.Lock()
+		if transactionMetadata.solid != solid {
+			transactionMetadata.solid = solid
+			if solid {
+				transactionMetadata.solidificationTimeMutex.Lock()
+				transactionMetadata.solidificationTime = time.Now()
+				transactionMetadata.solidificationTimeMutex.Unlock()
+			}
+
+			transactionMetadata.SetModified()
+
+			modified = true
+		}
+		transactionMetadata.solidMutex.Unlock()
+
+	} else {
+		transactionMetadata.solidMutex.RUnlock()
+	}
+
+	return
+}
+
+// SoldificationTime returns the time when the Transaction was marked to be solid.
+func (transactionMetadata *TransactionMetadata) SoldificationTime() time.Time {
+	transactionMetadata.solidificationTimeMutex.RLock()
+	defer transactionMetadata.solidificationTimeMutex.RUnlock()
+
+	return transactionMetadata.solidificationTime
+}
+
+// Bytes marshals the TransactionMetadata object into a sequence of bytes.
+func (transactionMetadata *TransactionMetadata) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	marshalUtil.WriteBytes(transactionMetadata.id.Bytes())
+	marshalUtil.WriteTime(transactionMetadata.solidificationTime)
+	marshalUtil.WriteBool(transactionMetadata.solid)
+
+	return marshalUtil.Bytes()
+}
+
+// String creates a human readable version of the metadata (for debug purposes).
+func (transactionMetadata *TransactionMetadata) String() string {
+	return stringify.Struct("transaction.TransactionMetadata",
+		stringify.StructField("payloadId", transactionMetadata.Id()),
+		stringify.StructField("solid", transactionMetadata.Solid()),
+		stringify.StructField("solidificationTime", transactionMetadata.SoldificationTime()),
+	)
+}
+
+// GetStorageKey returns the key that is used to identify the TransactionMetadata in the objectstorage.
+func (transactionMetadata *TransactionMetadata) GetStorageKey() []byte {
+	return transactionMetadata.id.Bytes()
+}
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+func (transactionMetadata *TransactionMetadata) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// MarshalBinary marshals the TransactionMetadata object into a sequence of bytes and matches the encoding.BinaryMarshaler
+// interface.
+func (transactionMetadata *TransactionMetadata) MarshalBinary() ([]byte, error) {
+	return transactionMetadata.Bytes(), nil
+}
+
+// UnmarshalBinary restores the values of a TransactionMetadata object from a sequence of bytes and matches the
+// encoding.BinaryUnmarshaler interface.
+func (transactionMetadata *TransactionMetadata) UnmarshalBinary(data []byte) (err error) {
+	_, err, _ = TransactionMetadataFromBytes(data, transactionMetadata)
+
+	return
+}
+
+// CachedTransactionMetadata is a wrapper for the object storage, that takes care of type casting the TransactionMetadata objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of TransactionMetadata, without having to manually type cast over and over again.
+type CachedTransactionMetadata struct {
+	objectstorage.CachedObject
+}
+
+// Retain overrides the underlying method to return a new CachedTransactionMetadata instead of a generic CachedObject.
+func (cachedTransactionMetadata *CachedTransactionMetadata) Retain() *CachedTransactionMetadata {
+	return &CachedTransactionMetadata{cachedTransactionMetadata.CachedObject.Retain()}
+}
+
+// Consume  overrides the underlying method to use a CachedTransactionMetadata object instead of a generic CachedObject in the
+// consumer).
+func (cachedTransactionMetadata *CachedTransactionMetadata) Consume(consumer func(metadata *TransactionMetadata)) bool {
+	return cachedTransactionMetadata.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*TransactionMetadata))
+	})
+}
+
+// Unwrap provides a way to retrieve a type casted version of the underlying object.
+func (cachedTransactionMetadata *CachedTransactionMetadata) Unwrap() *TransactionMetadata {
+	if untypedTransaction := cachedTransactionMetadata.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*TransactionMetadata); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go b/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go
new file mode 100644
index 0000000000000000000000000000000000000000..9e0c15feea79b708abdc6db89d573444d98da3ea
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go
@@ -0,0 +1,213 @@
+package tangle
+
+import (
+	"sync"
+	"time"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
+)
+
+// TransactionOutputMetadata contains the information of a transaction output, that are based on our local perception of things (i.e. if it
+// is solid, or when we it became solid).
+type TransactionOutputMetadata struct {
+	objectstorage.StorableObjectFlags
+
+	id                 transaction.OutputId
+	solid              bool
+	solidificationTime time.Time
+
+	solidMutex              sync.RWMutex
+	solidificationTimeMutex sync.RWMutex
+}
+
+// NewOutputMetadata is the constructor for the TransactionOutputMetadata type.
+func NewTransactionOutputMetadata(outputId transaction.OutputId) *TransactionOutputMetadata {
+	return &TransactionOutputMetadata{
+		id: outputId,
+	}
+}
+
+// TransactionOutputMetadataFromBytes unmarshals a TransactionOutputMetadata object from a sequence of bytes.
+// It either creates a new object or fills the optionally provided object with the parsed information.
+func TransactionOutputMetadataFromBytes(bytes []byte, optionalTargetObject ...*TransactionOutputMetadata) (result *TransactionOutputMetadata, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &TransactionOutputMetadata{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to TransactionOutputMetadataFromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.id, err = transaction.ParseOutputId(marshalUtil); err != nil {
+		return
+	}
+	if result.solidificationTime, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	if result.solid, err = marshalUtil.ReadBool(); err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// TransactionOutputMetadataFromStorage is the factory method for TransactionOutputMetadata objects stored in the objectstorage. The bytes and the content
+// will be filled by the objectstorage, by subsequently calling MarshalBinary.
+func TransactionOutputMetadataFromStorage(storageKey []byte) objectstorage.StorableObject {
+	result := &TransactionOutputMetadata{}
+
+	var err error
+	if result.id, err = transaction.ParseOutputId(marshalutil.New(storageKey)); err != nil {
+		panic(err)
+	}
+
+	return result
+}
+
+// Parse is a wrapper for simplified unmarshaling of TransactionOutputMetadata objects from a byte stream using the marshalUtil package.
+func ParseTransactionOutputMetadata(marshalUtil *marshalutil.MarshalUtil) (*TransactionOutputMetadata, error) {
+	if outputMetadata, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return TransactionOutputMetadataFromBytes(data) }); err != nil {
+		return nil, err
+	} else {
+		return outputMetadata.(*TransactionOutputMetadata), nil
+	}
+}
+
+// OutputId returns the id of the Output that this TransactionOutputMetadata is associated to.
+func (transactionOutputMetadata *TransactionOutputMetadata) Id() transaction.OutputId {
+	return transactionOutputMetadata.id
+}
+
+// Solid returns true if the Output has been marked as solid.
+func (transactionOutputMetadata *TransactionOutputMetadata) Solid() (result bool) {
+	transactionOutputMetadata.solidMutex.RLock()
+	result = transactionOutputMetadata.solid
+	transactionOutputMetadata.solidMutex.RUnlock()
+
+	return
+}
+
+// SetSolid marks a Output as either solid or not solid.
+// It returns true if the solid flag was changes and automatically updates the solidificationTime as well.
+func (transactionOutputMetadata *TransactionOutputMetadata) SetSolid(solid bool) (modified bool) {
+	transactionOutputMetadata.solidMutex.RLock()
+	if transactionOutputMetadata.solid != solid {
+		transactionOutputMetadata.solidMutex.RUnlock()
+
+		transactionOutputMetadata.solidMutex.Lock()
+		if transactionOutputMetadata.solid != solid {
+			transactionOutputMetadata.solid = solid
+			if solid {
+				transactionOutputMetadata.solidificationTimeMutex.Lock()
+				transactionOutputMetadata.solidificationTime = time.Now()
+				transactionOutputMetadata.solidificationTimeMutex.Unlock()
+			}
+
+			transactionOutputMetadata.SetModified()
+
+			modified = true
+		}
+		transactionOutputMetadata.solidMutex.Unlock()
+
+	} else {
+		transactionOutputMetadata.solidMutex.RUnlock()
+	}
+
+	return
+}
+
+// SoldificationTime returns the time when the Output was marked to be solid.
+func (transactionOutputMetadata *TransactionOutputMetadata) SoldificationTime() time.Time {
+	transactionOutputMetadata.solidificationTimeMutex.RLock()
+	defer transactionOutputMetadata.solidificationTimeMutex.RUnlock()
+
+	return transactionOutputMetadata.solidificationTime
+}
+
+// Bytes marshals the TransactionOutputMetadata object into a sequence of bytes.
+func (transactionOutputMetadata *TransactionOutputMetadata) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	marshalUtil.WriteBytes(transactionOutputMetadata.id.Bytes())
+	marshalUtil.WriteTime(transactionOutputMetadata.solidificationTime)
+	marshalUtil.WriteBool(transactionOutputMetadata.solid)
+
+	return marshalUtil.Bytes()
+}
+
+// String creates a human readable version of the metadata (for debug purposes).
+func (transactionOutputMetadata *TransactionOutputMetadata) String() string {
+	return stringify.Struct("transaction.TransactionOutputMetadata",
+		stringify.StructField("payloadId", transactionOutputMetadata.Id()),
+		stringify.StructField("solid", transactionOutputMetadata.Solid()),
+		stringify.StructField("solidificationTime", transactionOutputMetadata.SoldificationTime()),
+	)
+}
+
+// GetStorageKey returns the key that is used to identify the TransactionOutputMetadata in the objectstorage.
+func (transactionOutputMetadata *TransactionOutputMetadata) GetStorageKey() []byte {
+	return transactionOutputMetadata.id.Bytes()
+}
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+func (transactionOutputMetadata *TransactionOutputMetadata) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// MarshalBinary marshals the TransactionOutputMetadata object into a sequence of bytes and matches the encoding.BinaryMarshaler
+// interface.
+func (transactionOutputMetadata *TransactionOutputMetadata) MarshalBinary() ([]byte, error) {
+	return transactionOutputMetadata.Bytes(), nil
+}
+
+// UnmarshalBinary restores the values of a TransactionOutputMetadata object from a sequence of bytes and matches the
+// encoding.BinaryUnmarshaler interface.
+func (transactionOutputMetadata *TransactionOutputMetadata) UnmarshalBinary(data []byte) (err error) {
+	_, err, _ = TransactionOutputMetadataFromBytes(data, transactionOutputMetadata)
+
+	return
+}
+
+// CachedTransactionOutputMetadata is a wrapper for the object storage, that takes care of type casting the TransactionOutputMetadata objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of TransactionOutputMetadata, without having to manually type cast over and over again.
+type CachedTransactionOutputMetadata struct {
+	objectstorage.CachedObject
+}
+
+// Retain overrides the underlying method to return a new CachedTransactionOutputMetadata instead of a generic CachedObject.
+func (cachedOutputMetadata *CachedTransactionOutputMetadata) Retain() *CachedTransactionOutputMetadata {
+	return &CachedTransactionOutputMetadata{cachedOutputMetadata.CachedObject.Retain()}
+}
+
+// Consume  overrides the underlying method to use a CachedTransactionOutputMetadata object instead of a generic CachedObject in the
+// consumer).
+func (cachedOutputMetadata *CachedTransactionOutputMetadata) Consume(consumer func(outputMetadata *TransactionOutputMetadata)) bool {
+	return cachedOutputMetadata.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*TransactionOutputMetadata))
+	})
+}
+
+// Unwrap provides a way to retrieve a type casted version of the underlying object.
+func (cachedOutputMetadata *CachedTransactionOutputMetadata) Unwrap() *TransactionOutputMetadata {
+	if untypedTransaction := cachedOutputMetadata.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*TransactionOutputMetadata); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/test/payload_test.go b/packages/binary/valuetransfer/test/payload_test.go
deleted file mode 100644
index dcee4c24f36a1c9214723b297144caae70857301..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfer/test/payload_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package test
-
-import (
-	"fmt"
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance/color"
-	valuepayload "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/inputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/outputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/signatures"
-	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transferoutput/id"
-)
-
-func ExamplePayload() {
-	// 1. create value transfer (user provides this)
-	valueTransfer := transfer.New(
-		// inputs
-		inputs.New(
-			transferoutputid.New(address.Random(), transferid.New([]byte("transfer1"))),
-			transferoutputid.New(address.Random(), transferid.New([]byte("transfer2"))),
-		),
-
-		// outputs
-		outputs.New(map[address.Address][]*coloredbalance.ColoredBalance{
-			address.Random(): {
-				coloredbalance.New(color.IOTA, 1337),
-			},
-		}),
-	)
-
-	// 2. create value payload (the ontology creates this and wraps the user provided transfer accordingly)
-	valuePayload := valuepayload.New(
-		// trunk in "value transfer ontology" (filled by ontology tipSelector)
-		payloadid.Genesis,
-
-		// branch in "value transfer ontology"  (filled by ontology tipSelector)
-		payloadid.Genesis,
-
-		// value transfer
-		valueTransfer,
-	)
-
-	// 3. build actual transaction (the base layer creates this and wraps the ontology provided payload)
-	tx := transaction.New(
-		// trunk in "network tangle" ontology (filled by tipSelector)
-		transaction.EmptyId,
-
-		// branch in "network tangle" ontology (filled by tipSelector)
-		transaction.EmptyId,
-
-		// issuer of the transaction (signs automatically)
-		ed25119.GenerateKeyPair(),
-
-		// the time when the transaction was created
-		time.Now(),
-
-		// the ever increasing sequence number of this transaction
-		0,
-
-		// payload
-		valuePayload,
-	)
-
-	fmt.Println(tx)
-}
-
-func TestPayload(t *testing.T) {
-	addressKeyPair1 := ed25119.GenerateKeyPair()
-	addressKeyPair2 := ed25119.GenerateKeyPair()
-
-	originalPayload := valuepayload.New(
-		payloadid.Genesis,
-		payloadid.Genesis,
-		transfer.New(
-			inputs.New(
-				transferoutputid.New(address.FromED25519PubKey(addressKeyPair1.PublicKey), transferid.New([]byte("transfer1"))),
-				transferoutputid.New(address.FromED25519PubKey(addressKeyPair2.PublicKey), transferid.New([]byte("transfer2"))),
-			),
-
-			outputs.New(map[address.Address][]*coloredbalance.ColoredBalance{
-				address.Random(): {
-					coloredbalance.New(color.IOTA, 1337),
-				},
-			}),
-		).Sign(
-			signatures.ED25519(addressKeyPair1),
-		),
-	)
-
-	assert.Equal(t, false, originalPayload.GetTransfer().SignaturesValid())
-
-	originalPayload.GetTransfer().Sign(
-		signatures.ED25519(addressKeyPair2),
-	)
-
-	assert.Equal(t, true, originalPayload.GetTransfer().SignaturesValid())
-
-	clonedPayload1, err, _ := valuepayload.FromBytes(originalPayload.Bytes())
-	if err != nil {
-		panic(err)
-	}
-
-	assert.Equal(t, originalPayload.GetId(), clonedPayload1.GetId())
-	assert.Equal(t, true, clonedPayload1.GetTransfer().SignaturesValid())
-
-	clonedPayload2, err, _ := valuepayload.FromBytes(clonedPayload1.Bytes())
-	if err != nil {
-		panic(err)
-	}
-
-	assert.Equal(t, originalPayload.GetId(), clonedPayload2.GetId())
-	assert.Equal(t, true, clonedPayload2.GetTransfer().SignaturesValid())
-}
diff --git a/packages/binary/valuetransfer/transaction/id.go b/packages/binary/valuetransfer/transaction/id.go
new file mode 100644
index 0000000000000000000000000000000000000000..396b5d28abda3c24edaa508a603fb4c585fb81f5
--- /dev/null
+++ b/packages/binary/valuetransfer/transaction/id.go
@@ -0,0 +1,86 @@
+package transaction
+
+import (
+	"crypto/rand"
+	"fmt"
+
+	"github.com/mr-tron/base58"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+)
+
+// Id is the data type that represents the identifier for a Transaction.
+type Id [IdLength]byte
+
+// IdFromBase58 creates an id from a base58 encoded string.
+func IdFromBase58(base58String string) (id Id, err error) {
+	// decode string
+	bytes, err := base58.Decode(base58String)
+	if err != nil {
+		return
+	}
+
+	// sanitize input
+	if len(bytes) != IdLength {
+		err = fmt.Errorf("base58 encoded string does not match the length of a transaction id")
+
+		return
+	}
+
+	// copy bytes to result
+	copy(id[:], bytes)
+
+	return
+}
+
+// IdFromBytes unmarshals an Id from a sequence of bytes.
+func IdFromBytes(bytes []byte) (result Id, err error, consumedBytes int) {
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if idBytes, idErr := marshalUtil.ReadBytes(IdLength); idErr != nil {
+		err = idErr
+
+		return
+	} else {
+		copy(result[:], idBytes)
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// Parse is a wrapper for simplified unmarshaling of Ids from a byte stream using the marshalUtil package.
+func ParseId(marshalUtil *marshalutil.MarshalUtil) (Id, error) {
+	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) }); err != nil {
+		return Id{}, err
+	} else {
+		return id.(Id), nil
+	}
+}
+
+// Random creates a random address, which can for example be used in unit tests.
+func RandomId() (id Id) {
+	// generate a random sequence of bytes
+	idBytes := make([]byte, IdLength)
+	if _, err := rand.Read(idBytes); err != nil {
+		panic(err)
+	}
+
+	// copy the generated bytes into the result
+	copy(id[:], idBytes)
+
+	return
+}
+
+// Bytes marshals the Id into a sequence of bytes.
+func (id Id) Bytes() []byte {
+	return id[:]
+}
+
+// String creates a human readable version of the Id (for debug purposes).
+func (id Id) String() string {
+	return base58.Encode(id[:])
+}
+
+// IdLength contains the amount of bytes that a marshaled version of the Id contains.
+const IdLength = 32
diff --git a/packages/binary/valuetransfer/payload/transfer/inputs/inputs.go b/packages/binary/valuetransfer/transaction/inputs.go
similarity index 50%
rename from packages/binary/valuetransfer/payload/transfer/inputs/inputs.go
rename to packages/binary/valuetransfer/transaction/inputs.go
index 1d107dc7a502207db185db142780fdc5dbc854a2..49f07e965add261dd8518f7f125e0489895e17ad 100644
--- a/packages/binary/valuetransfer/payload/transfer/inputs/inputs.go
+++ b/packages/binary/valuetransfer/transaction/inputs.go
@@ -1,29 +1,26 @@
-package inputs
+package transaction
 
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transferoutput/id"
 )
 
 type Inputs struct {
 	*orderedmap.OrderedMap
 }
 
-func New(transferOutputIds ...transferoutputid.Id) (inputs *Inputs) {
+func NewInputs(outputIds ...OutputId) (inputs *Inputs) {
 	inputs = &Inputs{orderedmap.New()}
-	for _, transferOutputId := range transferOutputIds {
-		inputs.Add(transferOutputId)
+	for _, outputId := range outputIds {
+		inputs.Add(outputId)
 	}
 
 	return
 }
 
-func FromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) {
-	inputs = New()
+func InputsFromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) {
+	inputs = NewInputs()
 
 	marshalUtil := marshalutil.New(bytes)
 	inputCount, err := marshalUtil.ReadUint32()
@@ -39,13 +36,18 @@ func FromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) {
 			return
 		}
 
-		transferIdBytes, readErr := marshalUtil.ReadBytes(transferid.Length)
+		idBytes, readErr := marshalUtil.ReadBytes(IdLength)
 		if readErr != nil {
 			err = readErr
 
 			return
 		}
-		transferId := id.New(transferIdBytes)
+		id, idErr, _ := IdFromBytes(idBytes)
+		if idErr != nil {
+			err = idErr
+
+			return
+		}
 
 		addressMap, addressExists := inputs.Get(readAddress)
 		if !addressExists {
@@ -53,7 +55,7 @@ func FromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) {
 
 			inputs.Set(readAddress, addressMap)
 		}
-		addressMap.(*orderedmap.OrderedMap).Set(transferId, transferoutputid.New(readAddress, transferId))
+		addressMap.(*orderedmap.OrderedMap).Set(id, NewOutputId(readAddress, id))
 	}
 
 	consumedBytes = marshalUtil.ReadOffset()
@@ -61,9 +63,9 @@ func FromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) {
 	return
 }
 
-func (inputs *Inputs) Add(input transferoutputid.Id) *Inputs {
-	inputAddress := input.GetAddress()
-	transferId := input.GetTransferId()
+func (inputs *Inputs) Add(input OutputId) *Inputs {
+	inputAddress := input.Address()
+	transactionId := input.TransactionId()
 
 	addressMap, addressExists := inputs.Get(inputAddress)
 	if !addressExists {
@@ -72,7 +74,7 @@ func (inputs *Inputs) Add(input transferoutputid.Id) *Inputs {
 		inputs.Set(inputAddress, addressMap)
 	}
 
-	addressMap.(*orderedmap.OrderedMap).Set(transferId, input)
+	addressMap.(*orderedmap.OrderedMap).Set(transactionId, input)
 
 	return inputs
 }
@@ -82,10 +84,12 @@ func (inputs *Inputs) Bytes() (bytes []byte) {
 
 	marshalUtil.WriteSeek(4)
 	var inputCounter uint32
-	inputs.ForEach(func(transferOutputId transferoutputid.Id) {
-		marshalUtil.WriteBytes(transferOutputId.ToBytes())
+	inputs.ForEach(func(outputId OutputId) bool {
+		marshalUtil.WriteBytes(outputId.Bytes())
 
 		inputCounter++
+
+		return true
 	})
 	marshalUtil.WriteSeek(0)
 	marshalUtil.WriteUint32(inputCounter)
@@ -93,24 +97,34 @@ func (inputs *Inputs) Bytes() (bytes []byte) {
 	return marshalUtil.Bytes()
 }
 
-func (inputs *Inputs) ForEach(consumer func(transferOutputId transferoutputid.Id)) {
-	inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
-		value.(*orderedmap.OrderedMap).ForEach(func(key, value interface{}) bool {
-			consumer(value.(transferoutputid.Id))
-
-			return true
+func (inputs *Inputs) ForEach(consumer func(outputId OutputId) bool) bool {
+	return inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
+		return value.(*orderedmap.OrderedMap).ForEach(func(key, value interface{}) bool {
+			return consumer(value.(OutputId))
 		})
-
-		return true
 	})
 }
 
-func (inputs *Inputs) ForEachAddress(consumer func(currentAddress address.Address) bool) {
-	inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
+func (inputs *Inputs) ForEachAddress(consumer func(currentAddress address.Address) bool) bool {
+	return inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
 		return consumer(key.(address.Address))
 	})
 }
 
+func (inputs *Inputs) ForEachTransaction(consumer func(transactionId Id) bool) bool {
+	seenTransactions := make(map[Id]bool)
+
+	return inputs.ForEach(func(outputId OutputId) bool {
+		if currentTransactionId := outputId.TransactionId(); !seenTransactions[currentTransactionId] {
+			seenTransactions[currentTransactionId] = true
+
+			return consumer(currentTransactionId)
+		}
+
+		return true
+	})
+}
+
 func (inputs *Inputs) String() string {
 	if inputs == nil {
 		return "<nil>"
@@ -119,10 +133,12 @@ func (inputs *Inputs) String() string {
 	result := "[\n"
 
 	empty := true
-	inputs.ForEach(func(transferOutputId transferoutputid.Id) {
+	inputs.ForEach(func(outputId OutputId) bool {
 		empty = false
 
-		result += "    " + transferOutputId.String() + ",\n"
+		result += "    " + outputId.String() + ",\n"
+
+		return true
 	})
 
 	if empty {
diff --git a/packages/binary/valuetransfer/transaction/output.go b/packages/binary/valuetransfer/transaction/output.go
new file mode 100644
index 0000000000000000000000000000000000000000..949434dcb5c97c5749ccde643e0a0c0fb43db4a7
--- /dev/null
+++ b/packages/binary/valuetransfer/transaction/output.go
@@ -0,0 +1,126 @@
+package transaction
+
+import (
+	"fmt"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance"
+)
+
+// Output represents the output of a Transaction and contains the balances and the identifiers for this output.
+type Output struct {
+	address       address.Address
+	transactionId Id
+	balances      []*balance.Balance
+
+	objectstorage.StorableObjectFlags
+	storageKey []byte
+}
+
+// NewOutput creates an Output that contains the balances and identifiers of a Transaction.
+func NewOutput(address address.Address, transactionId Id, balances []*balance.Balance) *Output {
+	return &Output{
+		address:       address,
+		transactionId: transactionId,
+		balances:      balances,
+
+		storageKey: marshalutil.New().WriteBytes(address.Bytes()).WriteBytes(transactionId.Bytes()).Bytes(),
+	}
+}
+
+// OutputFromStorage get's called when we restore a Output from the storage.
+// In contrast to other database models, it unmarshals some information from the key so we simply store the key before
+// it gets handed over to UnmarshalBinary (by the ObjectStorage).
+func OutputFromStorage(keyBytes []byte) objectstorage.StorableObject {
+	return &Output{
+		storageKey: marshalutil.New(keyBytes).Bytes(true),
+	}
+}
+
+// Address returns the address that this output belongs to.
+func (output *Output) Address() address.Address {
+	return output.address
+}
+
+// TransactionId returns the id of the Transaction, that created this output.
+func (output *Output) TransactionId() Id {
+	return output.transactionId
+}
+
+// Balances returns the colored balances (color + balance) that this output contains.
+func (output *Output) Balances() []*balance.Balance {
+	return output.balances
+}
+
+// MarshalBinary marshals the balances into a sequence of bytes - the address and transaction id are stored inside the key
+// and are ignored here.
+func (output *Output) MarshalBinary() (data []byte, err error) {
+	// determine amount of balances in the output
+	balanceCount := len(output.balances)
+
+	// initialize helper
+	marshalUtil := marshalutil.New(4 + balanceCount*balance.Length)
+
+	// marshal the amount of balances
+	marshalUtil.WriteUint32(uint32(balanceCount))
+
+	// marshal balances
+	for _, balance := range output.balances {
+		marshalUtil.WriteBytes(balance.Bytes())
+	}
+
+	return
+}
+
+// UnmarshalBinary restores a Output from a serialized version in the ObjectStorage with parts of the object
+// being stored in its key rather than the content of the database to reduce storage requirements.
+func (output *Output) UnmarshalBinary(data []byte) (err error) {
+	// check if the storageKey has been set
+	if output.storageKey == nil {
+		return fmt.Errorf("missing storageKey when trying to unmarshal Output (it contains part of the information)")
+	}
+
+	// parse information from storageKey
+	storageKeyUnmarshaler := marshalutil.New(output.storageKey)
+	output.address, err = address.Parse(storageKeyUnmarshaler)
+	if err != nil {
+		return
+	}
+	output.transactionId, err = ParseId(storageKeyUnmarshaler)
+	if err != nil {
+		return
+	}
+
+	// parse information from content bytes
+	contentUnmarshaler := marshalutil.New(data)
+	balanceCount, err := contentUnmarshaler.ReadUint32()
+	if err != nil {
+		return
+	}
+	output.balances = make([]*balance.Balance, balanceCount)
+	for i := uint32(0); i < balanceCount; i++ {
+		output.balances[i], err = balance.Parse(contentUnmarshaler)
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// Update is disabled and panics if it ever gets called - it is required to match StorableObject interface.
+func (output *Output) Update(other objectstorage.StorableObject) {
+	panic("this object should never be updated")
+}
+
+// GetStorageKey returns the key that is used to store the object in the database.
+// It is required to match StorableObject interface.
+func (output *Output) GetStorageKey() []byte {
+	return output.storageKey
+}
+
+// define contract (ensure that the struct fulfills the given interface)
+var _ objectstorage.StorableObject = &Output{}
diff --git a/packages/binary/valuetransfer/transferoutput/transferoutput_test.go b/packages/binary/valuetransfer/transaction/output_test.go
similarity index 70%
rename from packages/binary/valuetransfer/transferoutput/transferoutput_test.go
rename to packages/binary/valuetransfer/transaction/output_test.go
index c7614b3bf67b4d52bd06c6154b100cd2b6385cae..305409e848b1e01d756a196f19f8380fce180d61 100644
--- a/packages/binary/valuetransfer/transferoutput/transferoutput_test.go
+++ b/packages/binary/valuetransfer/transaction/output_test.go
@@ -1,4 +1,4 @@
-package transferoutput
+package transaction
 
 import (
 	"testing"
diff --git a/packages/binary/valuetransfer/transaction/outputid.go b/packages/binary/valuetransfer/transaction/outputid.go
new file mode 100644
index 0000000000000000000000000000000000000000..891cd5c09b678dfd7f097e179a25ce3fb6460c3f
--- /dev/null
+++ b/packages/binary/valuetransfer/transaction/outputid.go
@@ -0,0 +1,71 @@
+package transaction
+
+import (
+	"github.com/mr-tron/base58"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+)
+
+// OutputId is the data type that represents the identifier for a Output.
+type OutputId [OutputIdLength]byte
+
+// NewOutputId is the constructor for the OutputId type.
+func NewOutputId(outputAddress address.Address, transactionId Id) (outputId OutputId) {
+	copy(outputId[:address.Length], outputAddress.Bytes())
+	copy(outputId[address.Length:], transactionId[:])
+
+	return
+}
+
+// OutputIdFromBytes unmarshals an OutputId from a sequence of bytes.
+func OutputIdFromBytes(bytes []byte) (result OutputId, err error, consumedBytes int) {
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if idBytes, idErr := marshalUtil.ReadBytes(OutputIdLength); idErr != nil {
+		err = idErr
+
+		return
+	} else {
+		copy(result[:], idBytes)
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// Parse is a wrapper for simplified unmarshaling of Ids from a byte stream using the marshalUtil package.
+func ParseOutputId(marshalUtil *marshalutil.MarshalUtil) (OutputId, error) {
+	if outputId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return OutputIdFromBytes(data) }); err != nil {
+		return OutputId{}, err
+	} else {
+		return outputId.(OutputId), nil
+	}
+}
+
+// Address returns the address part of an OutputId.
+func (outputId OutputId) Address() (address address.Address) {
+	copy(address[:], outputId[:])
+
+	return
+}
+
+// TransactionId returns the transaction id part of an OutputId.
+func (outputId OutputId) TransactionId() (transactionId Id) {
+	copy(transactionId[:], outputId[address.Length:])
+
+	return
+}
+
+// Bytes marshals the OutputId into a sequence of bytes.
+func (outputId OutputId) Bytes() []byte {
+	return outputId[:]
+}
+
+// String creates a human readable version of the OutputId (for debug purposes).
+func (outputId OutputId) String() string {
+	return base58.Encode(outputId[:])
+}
+
+// IdLength contains the amount of bytes that a marshaled version of the OutputId contains.
+const OutputIdLength = address.Length + IdLength
diff --git a/packages/binary/valuetransfer/payload/transfer/outputs/outputs.go b/packages/binary/valuetransfer/transaction/outputs.go
similarity index 79%
rename from packages/binary/valuetransfer/payload/transfer/outputs/outputs.go
rename to packages/binary/valuetransfer/transaction/outputs.go
index d32956f4fd94d848d6e808f4052ea27b1024ccf6..3166ac938e9e355218f9a8632c926229dac6a50f 100644
--- a/packages/binary/valuetransfer/payload/transfer/outputs/outputs.go
+++ b/packages/binary/valuetransfer/transaction/outputs.go
@@ -1,17 +1,17 @@
-package outputs
+package transaction
 
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance"
 )
 
 type Outputs struct {
 	*orderedmap.OrderedMap
 }
 
-func New(outputs map[address.Address][]*coloredbalance.ColoredBalance) (result *Outputs) {
+func NewOutputs(outputs map[address.Address][]*balance.Balance) (result *Outputs) {
 	result = &Outputs{orderedmap.New()}
 	for address, balances := range outputs {
 		result.Add(address, balances)
@@ -22,7 +22,7 @@ func New(outputs map[address.Address][]*coloredbalance.ColoredBalance) (result *
 
 // FromBytes reads the bytes and unmarshals the given information into an *Outputs object. It either creates a
 // new object, or uses the optional object provided in the arguments.
-func FromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs, err error, consumedBytes int) {
+func OutputsFromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -61,16 +61,16 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs,
 		}
 
 		// iterate the corresponding times and collect balances
-		coloredBalances := make([]*coloredbalance.ColoredBalance, balanceCount)
+		coloredBalances := make([]*balance.Balance, balanceCount)
 		for j := uint32(0); j < balanceCount; j++ {
-			coloredBalance, coloredBalanceErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return coloredbalance.FromBytes(data) })
+			coloredBalance, coloredBalanceErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return balance.FromBytes(data) })
 			if coloredBalanceErr != nil {
 				err = coloredBalanceErr
 
 				return
 			}
 
-			coloredBalances[j] = coloredBalance.(*coloredbalance.ColoredBalance)
+			coloredBalances[j] = coloredBalance.(*balance.Balance)
 		}
 
 		// add the gathered information as an output
@@ -83,15 +83,15 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs,
 	return
 }
 
-func (outputs *Outputs) Add(address address.Address, balances []*coloredbalance.ColoredBalance) *Outputs {
+func (outputs *Outputs) Add(address address.Address, balances []*balance.Balance) *Outputs {
 	outputs.Set(address, balances)
 
 	return outputs
 }
 
-func (outputs *Outputs) ForEach(consumer func(address address.Address, balances []*coloredbalance.ColoredBalance)) {
+func (outputs *Outputs) ForEach(consumer func(address address.Address, balances []*balance.Balance)) {
 	outputs.OrderedMap.ForEach(func(key, value interface{}) bool {
-		consumer(key.(address.Address), value.([]*coloredbalance.ColoredBalance))
+		consumer(key.(address.Address), value.([]*balance.Balance))
 
 		return true
 	})
@@ -107,7 +107,7 @@ func (outputs *Outputs) Bytes() []byte {
 	}
 
 	marshalUtil.WriteUint32(uint32(outputs.Size()))
-	outputs.ForEach(func(address address.Address, balances []*coloredbalance.ColoredBalance) {
+	outputs.ForEach(func(address address.Address, balances []*balance.Balance) {
 		marshalUtil.WriteBytes(address.Bytes())
 		marshalUtil.WriteUint32(uint32(len(balances)))
 
@@ -126,7 +126,7 @@ func (outputs *Outputs) String() string {
 
 	result := "[\n"
 	empty := true
-	outputs.ForEach(func(address address.Address, balances []*coloredbalance.ColoredBalance) {
+	outputs.ForEach(func(address address.Address, balances []*balance.Balance) {
 		empty = false
 
 		result += "    " + address.String() + ": [\n"
diff --git a/packages/binary/valuetransfer/payload/transfer/signatures/signatures.go b/packages/binary/valuetransfer/transaction/signatures.go
similarity index 66%
rename from packages/binary/valuetransfer/payload/transfer/signatures/signatures.go
rename to packages/binary/valuetransfer/transaction/signatures.go
index d07d1a43bdf62f827d52133792199f4250c9733e..26f3f45f468fc2a45e35fb56f3bacb70fbc3f362 100644
--- a/packages/binary/valuetransfer/payload/transfer/signatures/signatures.go
+++ b/packages/binary/valuetransfer/transaction/signatures.go
@@ -1,9 +1,10 @@
-package signatures
+package transaction
 
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
 )
 
 // Signatures represents a container for the address signatures of a value transfer.
@@ -14,7 +15,7 @@ type Signatures struct {
 }
 
 // New creates an empty container for the address signatures of a value transfer.
-func New() *Signatures {
+func NewSignatures() *Signatures {
 	return &Signatures{
 		orderedMap: orderedmap.New(),
 	}
@@ -22,7 +23,7 @@ func New() *Signatures {
 
 // FromBytes unmarshals a container with signatures from a sequence of bytes.
 // It either creates a new container or fills the optionally provided container with the parsed information.
-func FromBytes(bytes []byte, optionalTargetObject ...*Signatures) (result *Signatures, err error, consumedBytes int) {
+func SignaturesFromBytes(bytes []byte, optionalTargetObject ...*Signatures) (result *Signatures, err error, consumedBytes int) {
 	// determine the target object that will hold the unmarshaled information
 	switch len(optionalTargetObject) {
 	case 0:
@@ -46,15 +47,15 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Signatures) (result *Signa
 	for versionByte != 0 {
 		// perform signature scheme specific decoding
 		switch versionByte {
-		case VERSION_ED25519:
+		case signaturescheme.VERSION_ED25519:
 			marshalUtil.ReadSeek(-1)
-			signature, signatureErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ed25519SignatureFromBytes(data) })
+			signature, signatureErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return signaturescheme.Ed25519SignatureFromBytes(data) })
 			if signatureErr != nil {
 				err = signatureErr
 
 				return
 			}
-			typeCastedSignature := signature.(Signature)
+			typeCastedSignature := signature.(signaturescheme.Signature)
 
 			result.orderedMap.Set(typeCastedSignature.Address(), typeCastedSignature)
 		}
@@ -71,17 +72,17 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Signatures) (result *Signa
 	return
 }
 
-func (signatures *Signatures) Add(address address.Address, signature Signature) {
+func (signatures *Signatures) Add(address address.Address, signature signaturescheme.Signature) {
 	signatures.orderedMap.Set(address, signature)
 }
 
-func (signatures *Signatures) Get(address address.Address) (Signature, bool) {
+func (signatures *Signatures) Get(address address.Address) (signaturescheme.Signature, bool) {
 	signature, exists := signatures.orderedMap.Get(address)
 	if !exists {
 		return nil, false
 	}
 
-	return signature.(Signature), exists
+	return signature.(signaturescheme.Signature), exists
 }
 
 // Size returns the amount of signatures in this container.
@@ -91,9 +92,9 @@ func (signatures *Signatures) Size() int {
 
 // ForEach iterates through all signatures, calling the consumer for every found entry.
 // The iteration can be aborted by the consumer returning false
-func (signatures *Signatures) ForEach(consumer func(address address.Address, signature Signature) bool) {
+func (signatures *Signatures) ForEach(consumer func(address address.Address, signature signaturescheme.Signature) bool) {
 	signatures.orderedMap.ForEach(func(key, value interface{}) bool {
-		return consumer(key.(address.Address), value.(Signature))
+		return consumer(key.(address.Address), value.(signaturescheme.Signature))
 	})
 }
 
@@ -103,7 +104,7 @@ func (signatures *Signatures) Bytes() []byte {
 	marshalUtil := marshalutil.New()
 
 	// iterate through signatures and dump them
-	signatures.ForEach(func(address address.Address, signature Signature) bool {
+	signatures.ForEach(func(address address.Address, signature signaturescheme.Signature) bool {
 		marshalUtil.WriteBytes(signature.Bytes())
 
 		return true
@@ -115,3 +116,40 @@ func (signatures *Signatures) Bytes() []byte {
 	// return result
 	return marshalUtil.Bytes()
 }
+
+func (signatures *Signatures) String() string {
+	if signatures == nil {
+		return "<nil>"
+	}
+
+	result := "[\n"
+	empty := true
+	signatures.ForEach(func(address address.Address, signature signaturescheme.Signature) bool {
+		empty = false
+
+		result += "    " + address.String() + ": [\n"
+
+		/*
+			balancesEmpty := true
+			for _, balance := range balances {
+				balancesEmpty = false
+
+				result += "        " + balance.String() + ",\n"
+			}
+
+			if balancesEmpty {
+				result += "        <empty>\n"
+			}
+		*/
+
+		result += "    ]\n"
+
+		return true
+	})
+
+	if empty {
+		result += "    <empty>\n"
+	}
+
+	return result + "]"
+}
diff --git a/packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go b/packages/binary/valuetransfer/transaction/signatures_test.go
similarity index 65%
rename from packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go
rename to packages/binary/valuetransfer/transaction/signatures_test.go
index 214092e8a9fd33b77df6cb491d7a16a08a618b38..88589852620a60aef23c75b1e89a150c75f08ac3 100644
--- a/packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go
+++ b/packages/binary/valuetransfer/transaction/signatures_test.go
@@ -1,4 +1,4 @@
-package signatures
+package transaction
 
 import (
 	"testing"
@@ -7,15 +7,16 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
 )
 
 func TestSignatures(t *testing.T) {
 	dataToSign := []byte("test")
 
-	address1SigScheme := ED25519(ed25119.GenerateKeyPair())
-	address2SigScheme := ED25519(ed25119.GenerateKeyPair())
+	address1SigScheme := signaturescheme.ED25519(ed25119.GenerateKeyPair())
+	address2SigScheme := signaturescheme.ED25519(ed25119.GenerateKeyPair())
 
-	signatures := New()
+	signatures := NewSignatures()
 	signatures.Add(address1SigScheme.Address(), address1SigScheme.Sign(dataToSign))
 	signatures.Add(address2SigScheme.Address(), address2SigScheme.Sign(dataToSign))
 
@@ -25,13 +26,13 @@ func TestSignatures(t *testing.T) {
 
 	assert.Equal(t, 2, signatures.Size())
 
-	signatures.ForEach(func(address address.Address, signature Signature) bool {
+	signatures.ForEach(func(address address.Address, signature signaturescheme.Signature) bool {
 		assert.Equal(t, true, signature.IsValid(dataToSign))
 
 		return true
 	})
 
-	clonedSignatures, err, _ := FromBytes(signatures.Bytes())
+	clonedSignatures, err, _ := SignaturesFromBytes(signatures.Bytes())
 	if err != nil {
 		t.Error(err)
 
@@ -40,7 +41,7 @@ func TestSignatures(t *testing.T) {
 
 	assert.Equal(t, 2, clonedSignatures.Size())
 
-	clonedSignatures.ForEach(func(address address.Address, signature Signature) bool {
+	clonedSignatures.ForEach(func(address address.Address, signature signaturescheme.Signature) bool {
 		assert.Equal(t, true, signature.IsValid(dataToSign))
 
 		return true
diff --git a/packages/binary/valuetransfer/transaction/transaction.go b/packages/binary/valuetransfer/transaction/transaction.go
new file mode 100644
index 0000000000000000000000000000000000000000..705a62ab8278e0bda05a23b95914aa91188853f3
--- /dev/null
+++ b/packages/binary/valuetransfer/transaction/transaction.go
@@ -0,0 +1,339 @@
+package transaction
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+	"github.com/mr-tron/base58"
+	"golang.org/x/crypto/blake2b"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme"
+)
+
+// region IMPLEMENT Transaction ///////////////////////////////////////////////////////////////////////////////////////////
+
+type Transaction struct {
+	objectstorage.StorableObjectFlags
+
+	inputs     *Inputs
+	outputs    *Outputs
+	signatures *Signatures
+
+	id      *Id
+	idMutex sync.RWMutex
+
+	essenceBytes      []byte
+	essenceBytesMutex sync.RWMutex
+
+	signatureBytes      []byte
+	signatureBytesMutex sync.RWMutex
+
+	bytes      []byte
+	bytesMutex sync.RWMutex
+}
+
+func New(inputs *Inputs, outputs *Outputs) *Transaction {
+	return &Transaction{
+		inputs:     inputs,
+		outputs:    outputs,
+		signatures: NewSignatures(),
+	}
+}
+
+func FromBytes(bytes []byte, optionalTargetObject ...*Transaction) (result *Transaction, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &Transaction{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to OutputFromBytes")
+	}
+
+	// initialize helper
+	marshalUtil := marshalutil.New(bytes)
+
+	// unmarshal inputs
+	parsedInputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return InputsFromBytes(data) })
+	if err != nil {
+		return
+	}
+	result.inputs = parsedInputs.(*Inputs)
+
+	// unmarshal outputs
+	parsedOutputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return OutputsFromBytes(data) })
+	if err != nil {
+		return
+	}
+	result.outputs = parsedOutputs.(*Outputs)
+
+	// store essence bytes
+	essenceBytesCount := marshalUtil.ReadOffset()
+	result.essenceBytes = make([]byte, essenceBytesCount)
+	copy(result.essenceBytes, bytes[:essenceBytesCount])
+
+	// unmarshal outputs
+	parsedSignatures, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return SignaturesFromBytes(data) })
+	if err != nil {
+		return
+	}
+	result.signatures = parsedSignatures.(*Signatures)
+
+	// store signature bytes
+	signatureBytesCount := marshalUtil.ReadOffset() - essenceBytesCount
+	result.signatureBytes = make([]byte, signatureBytesCount)
+	copy(result.signatureBytes, bytes[essenceBytesCount:essenceBytesCount+signatureBytesCount])
+
+	// return the number of bytes we processed
+	consumedBytes = essenceBytesCount + signatureBytesCount
+
+	// store bytes, so we don't have to marshal manually
+	result.bytes = bytes[:consumedBytes]
+
+	return
+}
+
+func FromStorage(key []byte) *Transaction {
+	id, err, _ := IdFromBytes(key)
+	if err != nil {
+		panic(err)
+	}
+
+	return &Transaction{
+		id: &id,
+	}
+}
+
+func (transaction *Transaction) Id() Id {
+	// acquire lock for reading id
+	transaction.idMutex.RLock()
+
+	// return if id has been calculated already
+	if transaction.id != nil {
+		defer transaction.idMutex.RUnlock()
+
+		return *transaction.id
+	}
+
+	// switch to write lock
+	transaction.idMutex.RUnlock()
+	transaction.idMutex.Lock()
+	defer transaction.idMutex.Unlock()
+
+	// return if id has been calculated in the mean time
+	if transaction.id != nil {
+		return *transaction.id
+	}
+
+	// otherwise calculate the id
+	idBytes := blake2b.Sum256(transaction.Bytes())
+	id, err, _ := IdFromBytes(idBytes[:])
+	if err != nil {
+		panic(err)
+	}
+
+	// cache result for later calls
+	transaction.id = &id
+
+	return id
+}
+
+func (transaction *Transaction) Inputs() *Inputs {
+	return transaction.inputs
+}
+
+func (transaction *Transaction) SignaturesValid() bool {
+	signaturesValid := true
+	transaction.inputs.ForEachAddress(func(address address.Address) bool {
+		if signature, exists := transaction.signatures.Get(address); !exists || !signature.IsValid(transaction.EssenceBytes()) {
+			signaturesValid = false
+
+			return false
+		}
+
+		return true
+	})
+
+	return signaturesValid
+}
+
+func (transaction *Transaction) EssenceBytes() []byte {
+	// acquire read lock on essenceBytes
+	transaction.essenceBytesMutex.RLock()
+
+	// return essenceBytes if the object has been marshaled already
+	if transaction.essenceBytes != nil {
+		defer transaction.essenceBytesMutex.RUnlock()
+
+		return transaction.essenceBytes
+	}
+
+	// switch to write lock
+	transaction.essenceBytesMutex.RUnlock()
+	transaction.essenceBytesMutex.Lock()
+	defer transaction.essenceBytesMutex.Unlock()
+
+	// return essenceBytes if the object has been marshaled in the mean time
+	if essenceBytes := transaction.essenceBytes; essenceBytes != nil {
+		return essenceBytes
+	}
+
+	// create marshal helper
+	marshalUtil := marshalutil.New()
+
+	// marshal inputs
+	marshalUtil.WriteBytes(transaction.inputs.Bytes())
+
+	// marshal outputs
+	marshalUtil.WriteBytes(transaction.outputs.Bytes())
+
+	// store marshaled result
+	transaction.essenceBytes = marshalUtil.Bytes()
+
+	return transaction.essenceBytes
+}
+
+func (transaction *Transaction) SignatureBytes() []byte {
+	transaction.signatureBytesMutex.RLock()
+	if transaction.signatureBytes != nil {
+		defer transaction.signatureBytesMutex.RUnlock()
+
+		return transaction.signatureBytes
+	}
+
+	transaction.signatureBytesMutex.RUnlock()
+	transaction.signatureBytesMutex.Lock()
+	defer transaction.signatureBytesMutex.Unlock()
+
+	if transaction.signatureBytes != nil {
+		return transaction.signatureBytes
+	}
+
+	// generate signatures
+	transaction.signatureBytes = transaction.signatures.Bytes()
+
+	return transaction.signatureBytes
+}
+
+func (transaction *Transaction) Bytes() []byte {
+	// acquire read lock on bytes
+	transaction.bytesMutex.RLock()
+
+	// return bytes if the object has been marshaled already
+	if transaction.bytes != nil {
+		defer transaction.bytesMutex.RUnlock()
+
+		return transaction.bytes
+	}
+
+	// switch to write lock
+	transaction.bytesMutex.RUnlock()
+	transaction.bytesMutex.Lock()
+	defer transaction.bytesMutex.Unlock()
+
+	// return bytes if the object has been marshaled in the mean time
+	if bytes := transaction.bytes; bytes != nil {
+		return bytes
+	}
+
+	// create marshal helper
+	marshalUtil := marshalutil.New()
+
+	// marshal essence bytes
+	marshalUtil.WriteBytes(transaction.EssenceBytes())
+
+	// marshal signature bytes
+	marshalUtil.WriteBytes(transaction.SignatureBytes())
+
+	// store marshaled result
+	transaction.bytes = marshalUtil.Bytes()
+
+	return transaction.bytes
+}
+
+func (transaction *Transaction) Sign(signature signaturescheme.SignatureScheme) *Transaction {
+	transaction.signatures.Add(signature.Address(), signature.Sign(transaction.EssenceBytes()))
+
+	return transaction
+}
+
+func (transaction *Transaction) String() string {
+	id := transaction.Id()
+
+	return stringify.Struct("Transaction"+fmt.Sprintf("(%p)", transaction),
+		stringify.StructField("id", base58.Encode(id[:])),
+		stringify.StructField("inputs", transaction.inputs),
+		stringify.StructField("outputs", transaction.outputs),
+		stringify.StructField("signatures", transaction.signatures),
+	)
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// region IMPLEMENT StorableObject interface ///////////////////////////////////////////////////////////////////////////
+
+// define contract (ensure that the struct fulfills the given interface)
+var _ objectstorage.StorableObject = &Transaction{}
+
+func (transaction *Transaction) GetStorageKey() []byte {
+	id := transaction.Id()
+
+	return id[:]
+}
+
+func (transaction *Transaction) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// MarshalBinary returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler interface.
+func (transaction *Transaction) MarshalBinary() ([]byte, error) {
+	return transaction.Bytes(), nil
+}
+
+func (transaction *Transaction) UnmarshalBinary(bytes []byte) (err error) {
+	_, err, _ = FromBytes(bytes, transaction)
+
+	return
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CachedTransaction is a wrapper for the object storage, that takes care of type casting the Transaction objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of Transaction, without having to manually type cast over and over again.
+type CachedTransaction struct {
+	objectstorage.CachedObject
+}
+
+// Retain overrides the underlying method to return a new CachedTransaction instead of a generic CachedObject.
+func (cachedTransaction *CachedTransaction) Retain() *CachedTransaction {
+	return &CachedTransaction{cachedTransaction.CachedObject.Retain()}
+}
+
+// Consume  overrides the underlying method to use a CachedTransaction object instead of a generic CachedObject in the
+// consumer).
+func (cachedTransaction *CachedTransaction) Consume(consumer func(metadata *Transaction)) bool {
+	return cachedTransaction.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*Transaction))
+	})
+}
+
+// Unwrap provides a way to retrieve a type casted version of the underlying object.
+func (cachedTransaction *CachedTransaction) Unwrap() *Transaction {
+	if untypedTransaction := cachedTransaction.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*Transaction); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/transferoutput/id/id.go b/packages/binary/valuetransfer/transferoutput/id/id.go
deleted file mode 100644
index b8d9252e1267b0ab99387053ce1d6b76b35bee5f..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfer/transferoutput/id/id.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package id
-
-import (
-	"github.com/mr-tron/base58"
-
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-)
-
-type Id [Length]byte
-
-func New(outputAddress address.Address, transferId id.Id) (transferOutputId Id) {
-	copy(transferOutputId[:address.Length], outputAddress.Bytes())
-	copy(transferOutputId[address.Length:], transferId[:])
-
-	return
-}
-
-func FromBytes(bytes []byte) (transferOutputId Id) {
-	copy(transferOutputId[:], bytes)
-
-	return
-}
-
-func (transferOutputId Id) GetAddress() (address address.Address) {
-	copy(address[:], transferOutputId[:])
-
-	return
-}
-
-func (transferOutputId Id) GetTransferId() id.Id {
-	return id.New(transferOutputId[address.Length:])
-}
-
-func (transferOutputId Id) ToBytes() []byte {
-	return transferOutputId[:]
-}
-
-func (transferOutputId Id) String() string {
-	return "Id(" + base58.Encode(transferOutputId[:]) + ")"
-}
-
-const Length = address.Length + id.Length
diff --git a/packages/binary/valuetransfer/transferoutput/transferoutput.go b/packages/binary/valuetransfer/transferoutput/transferoutput.go
deleted file mode 100644
index b2808491ba8f9959dfc25e39444d102aaabafd7e..0000000000000000000000000000000000000000
--- a/packages/binary/valuetransfer/transferoutput/transferoutput.go
+++ /dev/null
@@ -1,127 +0,0 @@
-package transferoutput
-
-import (
-	"fmt"
-
-	"github.com/iotaledger/hive.go/objectstorage"
-
-	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
-	transferId "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
-)
-
-// TransferOutput represents the output of a transfer and it contains the balances and the identifiers for this output.
-type TransferOutput struct {
-	address    address.Address
-	transferId transferId.Id
-	balances   []*coloredbalance.ColoredBalance
-
-	objectstorage.StorableObjectFlags
-	storageKey []byte
-}
-
-// New creates a transfer output that contains the balances and identifiers of a successful transfer.
-func New(address address.Address, transferId transferId.Id, balances []*coloredbalance.ColoredBalance) *TransferOutput {
-	return &TransferOutput{
-		address:    address,
-		transferId: transferId,
-		balances:   balances,
-
-		storageKey: marshalutil.New().WriteBytes(address.Bytes()).WriteBytes(transferId.Bytes()).Bytes(),
-	}
-}
-
-// FromStorage get's called when we restore a TransferOutput from the storage.
-// In contrast to other database models, it unmarshals some information from the key so we simply store the key before
-// it gets handed over to UnmarshalBinary (by the ObjectStorage).
-func FromStorage(keyBytes []byte) objectstorage.StorableObject {
-	return &TransferOutput{
-		storageKey: marshalutil.New(keyBytes).Bytes(true),
-	}
-}
-
-// Address returns the address that this output belongs to.
-func (transferOutput *TransferOutput) Address() address.Address {
-	return transferOutput.address
-}
-
-// TransferId returns the transfer id, that created this output.
-func (transferOutput *TransferOutput) TransferId() transferId.Id {
-	return transferOutput.transferId
-}
-
-// Balances returns the colored balances (color + balance) that this output contains.
-func (transferOutput *TransferOutput) Balances() []*coloredbalance.ColoredBalance {
-	return transferOutput.balances
-}
-
-// MarshalBinary marshals the balances into a sequence of bytes - the address and transferId are stored inside the key
-// and are ignored here.
-func (transferOutput *TransferOutput) MarshalBinary() (data []byte, err error) {
-	// determine amount of balances in the output
-	balanceCount := len(transferOutput.balances)
-
-	// initialize helper
-	marshalUtil := marshalutil.New(4 + balanceCount*coloredbalance.Length)
-
-	// marshal the amount of balances
-	marshalUtil.WriteUint32(uint32(balanceCount))
-
-	// marshal balances
-	for _, balance := range transferOutput.balances {
-		marshalUtil.WriteBytes(balance.Bytes())
-	}
-
-	return
-}
-
-// UnmarshalBinary restores a TransferOutput from a serialized version in the ObjectStorage with parts of the object
-// being stored in its key rather than the content of the database to reduce storage requirements.
-func (transferOutput *TransferOutput) UnmarshalBinary(data []byte) (err error) {
-	// check if the storageKey has been set
-	if transferOutput.storageKey == nil {
-		return fmt.Errorf("missing storageKey when trying to unmarshal TransferOutput (it contains part of the information)")
-	}
-
-	// parse information from storageKey
-	storageKeyUnmarshaler := marshalutil.New(transferOutput.storageKey)
-	transferOutput.address, err = address.Parse(storageKeyUnmarshaler)
-	if err != nil {
-		return
-	}
-	transferOutput.transferId, err = transferId.Parse(storageKeyUnmarshaler)
-	if err != nil {
-		return
-	}
-
-	// parse information from content bytes
-	contentUnmarshaler := marshalutil.New(data)
-	balanceCount, err := contentUnmarshaler.ReadUint32()
-	if err != nil {
-		return
-	}
-	transferOutput.balances = make([]*coloredbalance.ColoredBalance, balanceCount)
-	for i := uint32(0); i < balanceCount; i++ {
-		transferOutput.balances[i], err = coloredbalance.Parse(contentUnmarshaler)
-		if err != nil {
-			return
-		}
-	}
-
-	return
-}
-
-// Update is disabled and panics if it ever gets called - it is required to match StorableObject interface.
-func (transferOutput *TransferOutput) Update(other objectstorage.StorableObject) {
-	panic("this object should never be updated")
-}
-
-// GetStorageKey returns the key that is used to store the object in the database.
-// It is required to match StorableObject interface.
-func (transferOutput *TransferOutput) GetStorageKey() []byte {
-	return transferOutput.storageKey
-}
-
-// define contract (ensure that the struct fulfills the given interface)
-var _ objectstorage.StorableObject = &TransferOutput{}
diff --git a/packages/gossip/manager.go b/packages/gossip/manager.go
index 24b72a251b8c507e86297dcf0bfa3e866d82489f..d3d6ccba7be2b5275b2bfde6b7f8250c0b0f6ba0 100644
--- a/packages/gossip/manager.go
+++ b/packages/gossip/manager.go
@@ -6,7 +6,7 @@ import (
 	"net"
 	"sync"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 
 	"github.com/golang/protobuf/proto"
 	pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
@@ -26,7 +26,7 @@ var (
 )
 
 // GetTransaction defines a function that returns the transaction data with the given hash.
-type GetTransaction func(transactionId transaction.Id) ([]byte, error)
+type GetTransaction func(transactionId message.Id) ([]byte, error)
 
 type Manager struct {
 	local          *peer.Local
@@ -256,7 +256,7 @@ func (m *Manager) handlePacket(data []byte, p *peer.Peer) error {
 		}
 		m.log.Debugw("received message", "type", "TRANSACTION_REQUEST", "id", p.ID())
 		// do something
-		txId, err, _ := transaction.IdFromBytes(msg.GetHash())
+		txId, err, _ := message.IdFromBytes(msg.GetHash())
 		if err != nil {
 			m.log.Debugw("error getting transaction", "hash", msg.GetHash(), "err", err)
 		}
diff --git a/packages/gossip/manager_test.go b/packages/gossip/manager_test.go
index afb6ee59d707b2f53ff0e0f4ad070bb67dce170b..f91adc10327492e87fcd33423c15653fa626a72c 100644
--- a/packages/gossip/manager_test.go
+++ b/packages/gossip/manager_test.go
@@ -7,7 +7,7 @@ import (
 	"time"
 
 	"github.com/golang/protobuf/proto"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
 	"github.com/iotaledger/goshimmer/packages/gossip/server"
 	"github.com/iotaledger/hive.go/autopeering/peer"
@@ -27,7 +27,7 @@ var (
 	testTxData = []byte("testTx")
 )
 
-func getTestTransaction(transaction.Id) ([]byte, error) { return testTxData, nil }
+func getTestTransaction(message.Id) ([]byte, error) { return testTxData, nil }
 
 func TestClose(t *testing.T) {
 	_, detach := newEventMock(t)
diff --git a/plugins/gossip/gossip.go b/plugins/gossip/gossip.go
index 7eaf015f846557a3d89f2a3c67e7e8a3c3163bf6..c32d37d8ceb990965a4b78c7f7c902e7cbe11a38 100644
--- a/plugins/gossip/gossip.go
+++ b/plugins/gossip/gossip.go
@@ -8,7 +8,7 @@ import (
 	"github.com/iotaledger/hive.go/autopeering/peer/service"
 	"github.com/iotaledger/hive.go/logger"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	gp "github.com/iotaledger/goshimmer/packages/gossip"
 	"github.com/iotaledger/goshimmer/packages/gossip/server"
 	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
@@ -76,10 +76,10 @@ func start(shutdownSignal <-chan struct{}) {
 	log.Info("Stopping " + name + " ...")
 }
 
-func getTransaction(transactionId transaction.Id) (bytes []byte, err error) {
+func getTransaction(transactionId message.Id) (bytes []byte, err error) {
 	log.Debugw("get tx from db", "id", transactionId.String())
 
-	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *transaction.Transaction) {
+	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *message.Transaction) {
 		bytes = transaction.Bytes()
 	}) {
 		err = fmt.Errorf("transaction not found: hash=%s", transactionId)
diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go
index 3d917c98bd06ff212ccdbc7eb72d1c8ca06bf0d0..ceca12f9e3a1e12300f408d8e2595b25aaa8eaf2 100644
--- a/plugins/gossip/plugin.go
+++ b/plugins/gossip/plugin.go
@@ -8,7 +8,7 @@ import (
 	"github.com/iotaledger/hive.go/logger"
 	"github.com/iotaledger/hive.go/node"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/gossip"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
@@ -77,16 +77,16 @@ func configureEvents() {
 	}))
 
 	// configure flow of outgoing transactions (gossip on solidification)
-	tangle.Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, transactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	tangle.Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, transactionMetadata *transactionmetadata.CachedTransactionMetadata) {
 		transactionMetadata.Release()
 
-		cachedTransaction.Consume(func(transaction *transaction.Transaction) {
+		cachedTransaction.Consume(func(transaction *message.Transaction) {
 			mgr.SendTransaction(transaction.Bytes())
 		})
 	}))
 
 	// request missing transactions
-	tangle.TransactionRequester.Events.SendRequest.Attach(events.NewClosure(func(transactionId transaction.Id) {
+	tangle.TransactionRequester.Events.SendRequest.Attach(events.NewClosure(func(transactionId message.Id) {
 		mgr.RequestTransaction(transactionId[:])
 	}))
 }
diff --git a/plugins/metrics/plugin.go b/plugins/metrics/plugin.go
index 28f3013c2697243faad32bb0cd8c8f9812912de4..501e05f9a2c41e36ac6838321afc629205dd0e4d 100644
--- a/plugins/metrics/plugin.go
+++ b/plugins/metrics/plugin.go
@@ -8,7 +8,7 @@ import (
 	"github.com/iotaledger/hive.go/node"
 	"github.com/iotaledger/hive.go/timeutil"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
 	"github.com/iotaledger/goshimmer/plugins/tangle"
@@ -18,7 +18,7 @@ var PLUGIN = node.NewPlugin("Metrics", node.Enabled, configure, run)
 
 func configure(plugin *node.Plugin) {
 	// increase received TPS counter whenever we receive a new transaction
-	tangle.Instance.Events.TransactionAttached.Attach(events.NewClosure(func(transaction *transaction.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
+	tangle.Instance.Events.TransactionAttached.Attach(events.NewClosure(func(transaction *message.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
 		transaction.Release()
 		metadata.Release()
 
diff --git a/plugins/spa/explorer_routes.go b/plugins/spa/explorer_routes.go
index e98cccbbd4e4c9c6672c17b6faabe8d97ac146be..7be338682180c3a68c11ce67f55b7b995c312537 100644
--- a/plugins/spa/explorer_routes.go
+++ b/plugins/spa/explorer_routes.go
@@ -4,7 +4,7 @@ import (
 	"net/http"
 	"sync"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/plugins/tangle"
 
 	"github.com/labstack/echo"
@@ -23,7 +23,7 @@ type ExplorerTx struct {
 	MWM                      int    `json:"mwm"`
 }
 
-func createExplorerTx(tx *transaction.Transaction) (*ExplorerTx, error) {
+func createExplorerTx(tx *message.Transaction) (*ExplorerTx, error) {
 	transactionId := tx.GetId()
 
 	txMetadata := tangle.Instance.GetTransactionMetadata(transactionId)
@@ -57,7 +57,7 @@ type SearchResult struct {
 
 func setupExplorerRoutes(routeGroup *echo.Group) {
 	routeGroup.GET("/tx/:hash", func(c echo.Context) (err error) {
-		transactionId, err := transaction.NewId(c.Param("hash"))
+		transactionId, err := message.NewId(c.Param("hash"))
 		if err != nil {
 			return
 		}
@@ -91,7 +91,7 @@ func setupExplorerRoutes(routeGroup *echo.Group) {
 		go func() {
 			defer wg.Done()
 
-			transactionId, err := transaction.NewId(search)
+			transactionId, err := message.NewId(search)
 			if err != nil {
 				return
 			}
@@ -115,8 +115,8 @@ func setupExplorerRoutes(routeGroup *echo.Group) {
 	})
 }
 
-func findTransaction(transactionId transaction.Id) (explorerTx *ExplorerTx, err error) {
-	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *transaction.Transaction) {
+func findTransaction(transactionId message.Id) (explorerTx *ExplorerTx, err error) {
+	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *message.Transaction) {
 		explorerTx, err = createExplorerTx(transaction)
 	}) {
 		err = errors.Wrapf(ErrNotFound, "tx hash: %s", transactionId.String())
diff --git a/plugins/spa/livefeed.go b/plugins/spa/livefeed.go
index 7a6674c934b0e3c0cde828f21a3cb55994ac00a6..adea985e1823779f4956d5ff7b08227cdd2dee7c 100644
--- a/plugins/spa/livefeed.go
+++ b/plugins/spa/livefeed.go
@@ -7,7 +7,7 @@ import (
 	"github.com/iotaledger/hive.go/events"
 	"github.com/iotaledger/hive.go/workerpool"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
 	"github.com/iotaledger/goshimmer/plugins/tangle"
@@ -19,7 +19,7 @@ var liveFeedWorkerPool *workerpool.WorkerPool
 
 func configureLiveFeed() {
 	liveFeedWorkerPool = workerpool.New(func(task workerpool.Task) {
-		task.Param(0).(*transaction.CachedTransaction).Consume(func(transaction *transaction.Transaction) {
+		task.Param(0).(*message.CachedTransaction).Consume(func(transaction *message.Transaction) {
 			sendToAllWSClient(&msg{MsgTypeTx, &tx{transaction.GetId().String(), 0}})
 		})
 
@@ -29,7 +29,7 @@ func configureLiveFeed() {
 
 func runLiveFeed() {
 	newTxRateLimiter := time.NewTicker(time.Second / 10)
-	notifyNewTx := events.NewClosure(func(tx *transaction.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
+	notifyNewTx := events.NewClosure(func(tx *message.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) {
 		metadata.Release()
 
 		select {
diff --git a/plugins/tangle/plugin.go b/plugins/tangle/plugin.go
index acc998fe3a0e6801f6337f0a120a91683fb0bab7..0b3f156dc0d154a602a032cd5ddd4c1e89b41449 100644
--- a/plugins/tangle/plugin.go
+++ b/plugins/tangle/plugin.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle"
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/tipselector"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser"
@@ -41,7 +41,7 @@ func configure(*node.Plugin) {
 	Instance = tangle.New(database.GetBadgerInstance(), storageprefix.MainNet)
 
 	// setup TransactionParser
-	TransactionParser.Events.TransactionParsed.Attach(events.NewClosure(func(transaction *transaction.Transaction, peer *peer.Peer) {
+	TransactionParser.Events.TransactionParsed.Attach(events.NewClosure(func(transaction *message.Transaction, peer *peer.Peer) {
 		// TODO: ADD PEER
 
 		Instance.AttachTransaction(transaction)
@@ -49,16 +49,16 @@ func configure(*node.Plugin) {
 
 	// setup TransactionRequester
 	Instance.Events.TransactionMissing.Attach(events.NewClosure(TransactionRequester.ScheduleRequest))
-	Instance.Events.MissingTransactionReceived.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	Instance.Events.MissingTransactionReceived.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
 		cachedTransactionMetadata.Release()
 
-		cachedTransaction.Consume(func(transaction *transaction.Transaction) {
+		cachedTransaction.Consume(func(transaction *message.Transaction) {
 			TransactionRequester.StopRequest(transaction.GetId())
 		})
 	}))
 
 	// setup TipSelector
-	Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
 		cachedTransactionMetadata.Release()
 
 		cachedTransaction.Consume(TipSelector.AddTip)
diff --git a/plugins/webapi/gtta/plugin.go b/plugins/webapi/gtta/plugin.go
index 4e05e8e3c7a0de8b3e523ce948de8bfec2ab23c9..ebebea7b8acf07d9f3d1af755c218c2c55e57644 100644
--- a/plugins/webapi/gtta/plugin.go
+++ b/plugins/webapi/gtta/plugin.go
@@ -3,7 +3,7 @@ package gtta
 import (
 	"net/http"
 
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/message"
 	"github.com/iotaledger/goshimmer/plugins/tangle"
 	"github.com/iotaledger/goshimmer/plugins/webapi"
 	"github.com/iotaledger/hive.go/node"
@@ -24,6 +24,6 @@ func Handler(c echo.Context) error {
 }
 
 type Response struct {
-	BranchTransaction transaction.Id `json:"branchTransaction"`
-	TrunkTransaction  transaction.Id `json:"trunkTransaction"`
+	BranchTransaction message.Id `json:"branchTransaction"`
+	TrunkTransaction  message.Id `json:"trunkTransaction"`
 }